ruby_llm 2.1.0 → 2.2.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 +162 -120
- data/app/assets/javascripts/ruby_llm/chat_message_controller.js +163 -0
- data/app/assets/javascripts/ruby_llm/dompurify.js +9 -0
- data/app/assets/javascripts/ruby_llm/marked.js +71 -0
- data/config/importmap.rb +5 -0
- data/lib/generators/ruby_llm/agent/agent_generator.rb +11 -3
- data/lib/generators/ruby_llm/agent/templates/agent.rb.tt +6 -3
- data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +201 -55
- data/lib/generators/ruby_llm/chat_ui/templates/agents/chat_agent.rb.tt +15 -0
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/answers_controller.rb.tt +31 -0
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt +16 -5
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt +27 -9
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/queued_messages_controller.rb.tt +14 -0
- data/lib/generators/ruby_llm/chat_ui/templates/javascript/chat_message_controller.js +1 -0
- data/lib/generators/ruby_llm/chat_ui/templates/jobs/chat_response_job.rb.tt +5 -8
- data/lib/generators/ruby_llm/chat_ui/templates/jobs/resume_interrupted_chats_job.rb.tt +7 -0
- data/lib/generators/ruby_llm/chat_ui/templates/migrations/add_reply_error_migration.rb.tt +5 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_files.html.erb.tt +15 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_form.html.erb.tt +5 -7
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_model_select.html.erb.tt +7 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/show.html.erb.tt +32 -3
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_assistant.html.erb.tt +17 -2
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_attachments.html.erb.tt +14 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_error.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_form.html.erb.tt +28 -18
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_queued_message.html.erb.tt +18 -0
- 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_calls.html.erb.tt +4 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_user.html.erb.tt +4 -2
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/tool_calls/_default.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/tool_results/_default.html.erb.tt +4 -2
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/questions/_question.html.erb.tt +101 -0
- data/lib/generators/ruby_llm/chat_ui/templates/test/agents/chat_agent_test.rb.tt +28 -0
- data/lib/generators/ruby_llm/chat_ui/templates/test/application_system_test_case.rb.tt +5 -0
- data/lib/generators/ruby_llm/chat_ui/templates/test/system/chats_test.rb.tt +240 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/answers/create.turbo_stream.erb.tt +6 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_files.html.erb.tt +15 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt +5 -7
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_model_select.html.erb.tt +7 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_questions.html.erb.tt +13 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt +29 -3
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_assistant.html.erb.tt +16 -2
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_attachments.html.erb.tt +14 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_error.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_form.html.erb.tt +26 -16
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_queued_message.html.erb.tt +8 -0
- 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_calls.html.erb.tt +4 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_user.html.erb.tt +3 -2
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt +1 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/tool_calls/_default.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/tool_results/_default.html.erb.tt +3 -2
- data/lib/generators/ruby_llm/chat_ui/templates/views/questions/_question.html.erb.tt +78 -0
- data/lib/generators/ruby_llm/evaluation/evaluation_generator.rb +43 -0
- data/lib/generators/ruby_llm/evaluation/templates/evaluation.rb.tt +9 -0
- data/lib/generators/ruby_llm/evaluation/templates/evaluation.yml.tt +4 -0
- data/lib/generators/ruby_llm/generator_helpers.rb +116 -3
- data/lib/generators/ruby_llm/install/install_generator.rb +5 -47
- data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +4 -0
- data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +5 -3
- data/lib/generators/ruby_llm/install/templates/create_ruby_llm_records_migration.rb.tt +41 -1
- data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +2 -1
- data/lib/generators/ruby_llm/install/templates/message_model.rb.tt +1 -2
- data/lib/generators/ruby_llm/judge/judge_generator.rb +35 -0
- data/lib/generators/ruby_llm/judge/templates/judge.rb.tt +2 -0
- data/lib/generators/ruby_llm/mcp/mcp_generator.rb +58 -0
- data/lib/generators/ruby_llm/mcp/templates/mcp.rb.tt +7 -0
- data/lib/generators/ruby_llm/provider/scaffold.rb +5 -12
- data/lib/generators/ruby_llm/provider/templates/core/provider.rb.erb +6 -0
- data/lib/generators/ruby_llm/provider/templates/gem/chat_spec.rb.erb +0 -1
- data/lib/generators/ruby_llm/provider/templates/gem/chat_streaming_spec.rb.erb +0 -1
- data/lib/generators/ruby_llm/provider/templates/gem/moderation_spec.rb.erb +1 -1
- data/lib/generators/ruby_llm/sandbox/sandbox_generator.rb +68 -0
- data/lib/generators/ruby_llm/sandbox/templates/sandbox.rb.tt +3 -0
- data/lib/generators/ruby_llm/skill/skill_generator.rb +48 -0
- data/lib/generators/ruby_llm/tool/templates/tailwind/tool_call.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/tool/templates/tailwind/tool_result.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/tool/templates/tool.rb.tt +0 -3
- data/lib/generators/ruby_llm/tool/templates/tool_call.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/tool/templates/tool_result.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/tool/tool_generator.rb +1 -5
- data/lib/generators/ruby_llm/upgrade/templates/upgrade_ruby_llm_to_2_2.rb.tt +164 -0
- data/lib/generators/ruby_llm/upgrade/upgrade_generator.rb +41 -3
- data/lib/ruby_llm/accounting/operation.rb +73 -0
- data/lib/ruby_llm/accounting/usage.rb +74 -24
- data/lib/ruby_llm/active_record/acts_as.rb +78 -11
- data/lib/ruby_llm/active_record/attachment_helpers.rb +10 -4
- data/lib/ruby_llm/active_record/batch.rb +67 -9
- data/lib/ruby_llm/active_record/chat_agent_hooks.rb +17 -0
- data/lib/ruby_llm/active_record/chat_approvals.rb +71 -0
- data/lib/ruby_llm/active_record/chat_cancellation.rb +106 -0
- data/lib/ruby_llm/active_record/chat_column_guard.rb +51 -0
- data/lib/ruby_llm/active_record/chat_file.rb +37 -0
- data/lib/ruby_llm/active_record/chat_files.rb +30 -0
- data/lib/ruby_llm/active_record/chat_helper.rb +73 -0
- data/lib/ruby_llm/active_record/chat_methods.rb +522 -277
- data/lib/ruby_llm/active_record/chat_placeholders.rb +67 -0
- data/lib/ruby_llm/active_record/chat_questions.rb +177 -0
- data/lib/ruby_llm/active_record/chat_queue.rb +120 -0
- data/lib/ruby_llm/active_record/chat_reply_error.rb +35 -0
- data/lib/ruby_llm/active_record/chat_run.rb +51 -0
- data/lib/ruby_llm/active_record/chat_tool_call_state.rb +77 -0
- data/lib/ruby_llm/active_record/chunk_methods.rb +49 -0
- data/lib/ruby_llm/active_record/claim_heartbeat.rb +149 -0
- data/lib/ruby_llm/active_record/file_store.rb +269 -0
- data/lib/ruby_llm/active_record/mcp_credential.rb +24 -9
- data/lib/ruby_llm/active_record/message_broadcasting.rb +85 -0
- data/lib/ruby_llm/active_record/message_methods.rb +83 -11
- data/lib/ruby_llm/active_record/model.rb +37 -9
- data/lib/ruby_llm/active_record/provider_file.rb +21 -4
- data/lib/ruby_llm/active_record/queued_message.rb +50 -0
- data/lib/ruby_llm/active_record/record.rb +3 -1
- data/lib/ruby_llm/active_record/run_claims.rb +608 -0
- data/lib/ruby_llm/active_record/solid_queue_shutdown.rb +33 -0
- data/lib/ruby_llm/active_record/system_instructions.rb +30 -0
- data/lib/ruby_llm/active_record/tool_call.rb +55 -4
- data/lib/ruby_llm/active_record/usage.rb +79 -9
- data/lib/ruby_llm/agent/callbacks.rb +98 -0
- data/lib/ruby_llm/agent/context_budget.rb +26 -0
- data/lib/ruby_llm/agent/join_callbacks.rb +28 -0
- data/lib/ruby_llm/agent/model_options.rb +56 -0
- data/lib/ruby_llm/agent/ownership.rb +22 -0
- data/lib/ruby_llm/agent/tool_conversion.rb +19 -0
- data/lib/ruby_llm/agent.rb +620 -236
- data/lib/ruby_llm/agent_tool.rb +91 -0
- data/lib/ruby_llm/aliases.json +84 -91
- data/lib/ruby_llm/attachment.rb +101 -20
- data/lib/ruby_llm/batch.rb +132 -46
- data/lib/ruby_llm/cached_content.rb +4 -4
- data/lib/ruby_llm/chat/agent_run.rb +22 -0
- data/lib/ruby_llm/chat/context_budget.rb +119 -0
- data/lib/ruby_llm/chat/file_references.rb +259 -0
- data/lib/ruby_llm/chat/message_queue.rb +56 -0
- data/lib/ruby_llm/chat/ownership.rb +18 -0
- data/lib/ruby_llm/chat/rescuable.rb +48 -0
- data/lib/ruby_llm/chat/run.rb +32 -0
- data/lib/ruby_llm/chat/tool_call_ids.rb +46 -0
- data/lib/ruby_llm/chat/tool_call_state.rb +115 -0
- data/lib/ruby_llm/chat/tool_concurrency.rb +90 -49
- data/lib/ruby_llm/chat/tool_search.rb +36 -1
- data/lib/ruby_llm/chat/tool_sequences.rb +84 -0
- data/lib/ruby_llm/chat.rb +1598 -332
- data/lib/ruby_llm/chunk.rb +27 -1
- data/lib/ruby_llm/citation.rb +2 -1
- data/lib/ruby_llm/code_execution/language.rb +28 -0
- data/lib/ruby_llm/code_execution/node_installation.rb +56 -0
- data/lib/ruby_llm/code_execution/packages.rb +241 -0
- data/lib/ruby_llm/code_execution/ruby_installation.rb +170 -0
- data/lib/ruby_llm/code_execution/workspace.rb +314 -0
- data/lib/ruby_llm/code_execution.rb +781 -0
- data/lib/ruby_llm/configuration.rb +151 -4
- data/lib/ruby_llm/content_filter.rb +68 -0
- data/lib/ruby_llm/cost.rb +68 -41
- data/lib/ruby_llm/downloaded_file.rb +10 -9
- data/lib/ruby_llm/embedding.rb +30 -45
- data/lib/ruby_llm/embedding_request.rb +1 -1
- data/lib/ruby_llm/error.rb +167 -14
- data/lib/ruby_llm/evaluation/dataset.rb +5 -4
- data/lib/ruby_llm/evaluation/evidence.rb +5 -3
- data/lib/ruby_llm/evaluation/report.rb +7 -3
- data/lib/ruby_llm/evaluation/result.rb +1 -1
- data/lib/ruby_llm/evaluation.rb +2 -1
- data/lib/ruby_llm/fallback.rb +32 -4
- data/lib/ruby_llm/file_store/converters/ocr.rb +28 -0
- data/lib/ruby_llm/file_store/converters/office/markup.rb +148 -0
- data/lib/ruby_llm/file_store/converters/office/outline.rb +193 -0
- data/lib/ruby_llm/file_store/converters/office/prose.rb +161 -0
- data/lib/ruby_llm/file_store/converters/office/workbook.rb +187 -0
- data/lib/ruby_llm/file_store/converters/office/zip.rb +149 -0
- data/lib/ruby_llm/file_store/converters/office.rb +106 -0
- data/lib/ruby_llm/file_store/converters/text.rb +27 -0
- data/lib/ruby_llm/file_store/converters.rb +84 -0
- data/lib/ruby_llm/file_store/directory.rb +190 -0
- data/lib/ruby_llm/file_store/memory.rb +75 -0
- data/lib/ruby_llm/file_store/tools/base.rb +99 -0
- data/lib/ruby_llm/file_store/tools/delete.rb +48 -0
- data/lib/ruby_llm/file_store/tools/edit.rb +155 -0
- data/lib/ruby_llm/file_store/tools/list.rb +57 -0
- data/lib/ruby_llm/file_store/tools/read.rb +120 -0
- data/lib/ruby_llm/file_store/tools/search.rb +208 -0
- data/lib/ruby_llm/file_store/tools/write.rb +40 -0
- data/lib/ruby_llm/file_store/tools.rb +39 -0
- data/lib/ruby_llm/file_store.rb +338 -0
- data/lib/ruby_llm/files/saving.rb +14 -0
- data/lib/ruby_llm/image.rb +67 -45
- data/lib/ruby_llm/input_required_error.rb +28 -0
- data/lib/ruby_llm/judge/builder.rb +6 -1
- data/lib/ruby_llm/judge.rb +1 -1
- data/lib/ruby_llm/judgment.rb +6 -24
- data/lib/ruby_llm/mcp/client.rb +67 -28
- data/lib/ruby_llm/mcp/collection.rb +39 -2
- data/lib/ruby_llm/mcp/content.rb +7 -2
- data/lib/ruby_llm/mcp/error.rb +8 -2
- data/lib/ruby_llm/mcp/http.rb +117 -37
- data/lib/ruby_llm/mcp/input_request.rb +3 -112
- data/lib/ruby_llm/mcp/input_required_error.rb +3 -21
- data/lib/ruby_llm/mcp/insufficient_scope_error.rb +27 -0
- data/lib/ruby_llm/mcp/listener.rb +7 -1
- data/lib/ruby_llm/mcp/oauth.rb +102 -39
- data/lib/ruby_llm/mcp/outages.rb +172 -0
- data/lib/ruby_llm/mcp/resource.rb +9 -6
- data/lib/ruby_llm/mcp/resource_template.rb +3 -2
- data/lib/ruby_llm/mcp/session_expired.rb +10 -2
- data/lib/ruby_llm/mcp/stdio.rb +66 -18
- data/lib/ruby_llm/mcp/task.rb +13 -40
- data/lib/ruby_llm/mcp/tool.rb +34 -11
- data/lib/ruby_llm/mcp.rb +123 -59
- data/lib/ruby_llm/message.rb +217 -23
- data/lib/ruby_llm/model/modalities.rb +1 -1
- data/lib/ruby_llm/model/pricing.rb +26 -4
- data/lib/ruby_llm/model/pricing_category.rb +38 -15
- data/lib/ruby_llm/model/pricing_tier.rb +3 -2
- data/lib/ruby_llm/model.rb +24 -6
- data/lib/ruby_llm/models/lookup.rb +37 -4
- data/lib/ruby_llm/models/registry.rb +4 -19
- data/lib/ruby_llm/models.json +36186 -27832
- data/lib/ruby_llm/models.rb +44 -39
- data/lib/ruby_llm/moderation.rb +10 -30
- data/lib/ruby_llm/ocr.rb +5 -22
- data/lib/ruby_llm/open_telemetry/attributes.rb +68 -19
- data/lib/ruby_llm/open_telemetry/content.rb +105 -0
- data/lib/ruby_llm/open_telemetry.rb +18 -4
- data/lib/ruby_llm/prompt/context.rb +2 -2
- data/lib/ruby_llm/prompt.rb +7 -5
- data/lib/ruby_llm/protocol/binary_streaming.rb +2 -8
- data/lib/ruby_llm/protocol/cache_lifetime.rb +22 -0
- data/lib/ruby_llm/protocol/native_content.rb +69 -0
- data/lib/ruby_llm/protocol/replayed_arguments.rb +28 -0
- data/lib/ruby_llm/protocol/stream_accumulator.rb +73 -33
- data/lib/ruby_llm/protocol/streaming.rb +105 -22
- data/lib/ruby_llm/protocol/tool_namespaces.rb +100 -0
- data/lib/ruby_llm/protocol/unsupported_operations.rb +42 -0
- data/lib/ruby_llm/protocol.rb +134 -109
- data/lib/ruby_llm/protocols/anthropic/batches.rb +41 -4
- data/lib/ruby_llm/protocols/anthropic/block_binding.rb +30 -0
- data/lib/ruby_llm/protocols/anthropic/chat.rb +135 -60
- data/lib/ruby_llm/protocols/anthropic/files.rb +9 -0
- data/lib/ruby_llm/protocols/anthropic/media.rb +14 -14
- data/lib/ruby_llm/protocols/anthropic/models.rb +16 -10
- data/lib/ruby_llm/protocols/anthropic/streaming.rb +43 -22
- data/lib/ruby_llm/protocols/anthropic/tools.rb +36 -24
- data/lib/ruby_llm/protocols/anthropic.rb +61 -6
- data/lib/ruby_llm/protocols/azure/speech/transcription.rb +125 -0
- data/lib/ruby_llm/protocols/azure/speech.rb +14 -0
- data/lib/ruby_llm/protocols/bedrock/batches.rb +26 -8
- data/lib/ruby_llm/protocols/bedrock/event_stream.rb +205 -0
- data/lib/ruby_llm/protocols/bedrock/files.rb +15 -6
- data/lib/ruby_llm/protocols/bedrock/guardrails.rb +1 -1
- data/lib/ruby_llm/protocols/bedrock/rerank.rb +2 -0
- data/lib/ruby_llm/protocols/chat_completions/chat.rb +158 -58
- data/lib/ruby_llm/protocols/chat_completions/content_filters.rb +45 -0
- data/lib/ruby_llm/protocols/chat_completions/embedding_batches.rb +23 -2
- data/lib/ruby_llm/protocols/chat_completions/embeddings.rb +26 -3
- data/lib/ruby_llm/protocols/chat_completions/images.rb +7 -9
- data/lib/ruby_llm/protocols/chat_completions/media.rb +20 -20
- data/lib/ruby_llm/protocols/chat_completions/moderation.rb +8 -5
- data/lib/ruby_llm/protocols/chat_completions/speech.rb +8 -2
- data/lib/ruby_llm/protocols/chat_completions/streaming.rb +38 -17
- data/lib/ruby_llm/protocols/chat_completions/think_tags.rb +148 -0
- data/lib/ruby_llm/protocols/chat_completions/tools.rb +15 -28
- data/lib/ruby_llm/protocols/chat_completions/transcription.rb +17 -2
- data/lib/ruby_llm/protocols/chat_completions.rb +4 -0
- data/lib/ruby_llm/protocols/cohere/batches.rb +15 -8
- data/lib/ruby_llm/protocols/cohere/chat.rb +47 -25
- data/lib/ruby_llm/protocols/cohere/datasets.rb +6 -2
- data/lib/ruby_llm/protocols/cohere/embeddings.rb +7 -5
- data/lib/ruby_llm/protocols/cohere/media.rb +7 -7
- data/lib/ruby_llm/protocols/cohere/rerank.rb +2 -1
- data/lib/ruby_llm/protocols/cohere/streaming.rb +26 -11
- data/lib/ruby_llm/protocols/cohere/tools.rb +17 -28
- data/lib/ruby_llm/protocols/cohere/transcription.rb +5 -4
- data/lib/ruby_llm/protocols/converse/batches.rb +2 -24
- data/lib/ruby_llm/protocols/converse/chat.rb +210 -75
- data/lib/ruby_llm/protocols/converse/media.rb +24 -22
- data/lib/ruby_llm/protocols/converse/reasoning.rb +23 -9
- data/lib/ruby_llm/protocols/converse/streaming.rb +68 -220
- data/lib/ruby_llm/protocols/converse.rb +17 -1
- data/lib/ruby_llm/protocols/deepgram/speech.rb +1 -2
- data/lib/ruby_llm/protocols/deepgram/streaming_transcription.rb +1 -3
- data/lib/ruby_llm/protocols/deepgram/transcription.rb +28 -19
- data/lib/ruby_llm/protocols/deepgram.rb +3 -2
- data/lib/ruby_llm/protocols/deepseek/files.rb +1 -1
- data/lib/ruby_llm/protocols/elevenlabs/assets.rb +6 -4
- data/lib/ruby_llm/protocols/elevenlabs/flows/images.rb +4 -0
- data/lib/ruby_llm/protocols/elevenlabs/speech.rb +3 -3
- data/lib/ruby_llm/protocols/elevenlabs/streaming_transcription.rb +5 -2
- data/lib/ruby_llm/protocols/elevenlabs/transcription.rb +6 -3
- data/lib/ruby_llm/protocols/files.rb +27 -12
- data/lib/ruby_llm/protocols/gemini/caches.rb +4 -4
- data/lib/ruby_llm/protocols/gemini/chat.rb +118 -51
- data/lib/ruby_llm/protocols/gemini/embedding_batches.rb +9 -5
- data/lib/ruby_llm/protocols/gemini/embeddings.rb +3 -3
- data/lib/ruby_llm/protocols/gemini/file_transcription.rb +4 -0
- data/lib/ruby_llm/protocols/gemini/files.rb +5 -4
- data/lib/ruby_llm/protocols/gemini/images.rb +11 -9
- data/lib/ruby_llm/protocols/gemini/live_transcription.rb +1 -1
- data/lib/ruby_llm/protocols/gemini/long_decode.rb +104 -0
- data/lib/ruby_llm/protocols/gemini/media.rb +31 -28
- data/lib/ruby_llm/protocols/gemini/retry_info.rb +29 -0
- data/lib/ruby_llm/protocols/gemini/schemas.rb +126 -0
- data/lib/ruby_llm/protocols/gemini/speech.rb +9 -3
- data/lib/ruby_llm/protocols/gemini/streaming.rb +37 -13
- data/lib/ruby_llm/protocols/gemini/tools.rb +18 -10
- data/lib/ruby_llm/protocols/gemini/transcription.rb +13 -9
- data/lib/ruby_llm/protocols/gemini/videos.rb +14 -3
- data/lib/ruby_llm/protocols/gemini.rb +16 -0
- data/lib/ruby_llm/protocols/gpustack/responses.rb +3 -3
- data/lib/ruby_llm/protocols/gpustack/tokenization.rb +14 -0
- data/lib/ruby_llm/protocols/interactions/chat.rb +29 -14
- data/lib/ruby_llm/protocols/interactions/content.rb +19 -14
- data/lib/ruby_llm/protocols/interactions/streaming.rb +1 -1
- data/lib/ruby_llm/protocols/interactions/tools.rb +10 -13
- data/lib/ruby_llm/protocols/interactions/videos.rb +99 -0
- data/lib/ruby_llm/protocols/interactions.rb +1 -0
- data/lib/ruby_llm/protocols/invoke_model/cohere_embeddings.rb +28 -10
- data/lib/ruby_llm/protocols/invoke_model/embedding_batches.rb +1 -1
- data/lib/ruby_llm/protocols/invoke_model/nova_embeddings.rb +45 -11
- data/lib/ruby_llm/protocols/invoke_model/stability_images.rb +5 -2
- data/lib/ruby_llm/protocols/invoke_model/titan_multimodal_embeddings.rb +11 -3
- data/lib/ruby_llm/protocols/invoke_model.rb +21 -2
- data/lib/ruby_llm/protocols/meta/transcription/stream.rb +108 -0
- data/lib/ruby_llm/protocols/meta/transcription.rb +76 -0
- data/lib/ruby_llm/protocols/minimax/files.rb +58 -0
- data/lib/ruby_llm/protocols/minimax/images.rb +54 -0
- data/lib/ruby_llm/protocols/minimax/speech.rb +53 -0
- data/lib/ruby_llm/protocols/minimax/transcription.rb +82 -0
- data/lib/ruby_llm/protocols/minimax/videos.rb +93 -0
- data/lib/ruby_llm/protocols/minimax.rb +21 -0
- data/lib/ruby_llm/protocols/mistral/conversations/chat.rb +44 -21
- data/lib/ruby_llm/protocols/mistral/files.rb +6 -4
- data/lib/ruby_llm/protocols/mistral/multi_completion.rb +15 -4
- data/lib/ruby_llm/protocols/moonshot/files.rb +48 -0
- data/lib/ruby_llm/protocols/openai/batches.rb +3 -2
- data/lib/ruby_llm/protocols/openai/decisions.rb +6 -4
- data/lib/ruby_llm/protocols/openai/files.rb +13 -6
- data/lib/ruby_llm/protocols/openrouter/batches.rb +13 -17
- data/lib/ruby_llm/protocols/openrouter/responses.rb +6 -10
- data/lib/ruby_llm/protocols/perplexity/agent.rb +19 -35
- data/lib/ruby_llm/protocols/perplexity/batches.rb +37 -0
- data/lib/ruby_llm/protocols/perplexity/files.rb +19 -9
- data/lib/ruby_llm/protocols/responses/approvals.rb +12 -11
- data/lib/ruby_llm/protocols/responses/chat.rb +165 -75
- data/lib/ruby_llm/protocols/responses/citation_markers.rb +167 -0
- data/lib/ruby_llm/protocols/responses/compaction.rb +2 -2
- data/lib/ruby_llm/protocols/responses/media.rb +17 -9
- data/lib/ruby_llm/protocols/responses/streaming.rb +71 -12
- data/lib/ruby_llm/protocols/responses/token_counting.rb +1 -1
- data/lib/ruby_llm/protocols/responses/tools.rb +48 -1
- data/lib/ruby_llm/protocols/responses.rb +21 -0
- data/lib/ruby_llm/protocols/system_one/responses.rb +9 -2
- data/lib/ruby_llm/protocols/vertexai/files.rb +4 -3
- data/lib/ruby_llm/protocols/vertexai/text_to_speech.rb +116 -0
- data/lib/ruby_llm/protocols/xai/files.rb +13 -6
- data/lib/ruby_llm/protocols/zai/files.rb +37 -0
- data/lib/ruby_llm/provider.rb +128 -31
- data/lib/ruby_llm/providers/anthropic.rb +5 -1
- data/lib/ruby_llm/providers/azure/chat.rb +17 -3
- data/lib/ruby_llm/providers/azure/chat_completions.rb +1 -0
- data/lib/ruby_llm/providers/azure/decisions.rb +17 -0
- data/lib/ruby_llm/providers/azure/embeddings.rb +1 -2
- data/lib/ruby_llm/providers/azure/media.rb +6 -6
- data/lib/ruby_llm/providers/azure/mistral.rb +17 -0
- data/lib/ruby_llm/providers/azure/models.rb +16 -2
- data/lib/ruby_llm/providers/azure/moderation.rb +16 -0
- data/lib/ruby_llm/providers/azure/responses.rb +9 -0
- data/lib/ruby_llm/providers/azure/videos.rb +2 -2
- data/lib/ruby_llm/providers/azure.rb +43 -1
- data/lib/ruby_llm/providers/bedrock/anthropic/batches.rb +32 -0
- data/lib/ruby_llm/providers/bedrock/anthropic.rb +134 -0
- data/lib/ruby_llm/providers/bedrock/auth.rb +12 -3
- data/lib/ruby_llm/providers/bedrock/connection.rb +19 -0
- data/lib/ruby_llm/providers/bedrock/mantle/anthropic.rb +5 -1
- data/lib/ruby_llm/providers/bedrock/mantle/chat_completions.rb +2 -2
- data/lib/ruby_llm/providers/bedrock/mantle/responses.rb +12 -2
- data/lib/ruby_llm/providers/bedrock/mantle/voxtral.rb +1 -1
- data/lib/ruby_llm/providers/bedrock/mantle.rb +7 -0
- data/lib/ruby_llm/providers/bedrock/models.rb +2 -9
- data/lib/ruby_llm/providers/bedrock.rb +51 -8
- data/lib/ruby_llm/providers/cohere.rb +5 -1
- data/lib/ruby_llm/providers/deepgram/chat.rb +25 -0
- data/lib/ruby_llm/providers/deepgram.rb +21 -2
- data/lib/ruby_llm/providers/deepseek/chat.rb +7 -16
- data/lib/ruby_llm/providers/deepseek/responses.rb +25 -8
- data/lib/ruby_llm/providers/deepseek.rb +5 -1
- data/lib/ruby_llm/providers/elevenlabs.rb +2 -1
- data/lib/ruby_llm/providers/gemini.rb +13 -1
- data/lib/ruby_llm/providers/gpustack/chat.rb +23 -3
- data/lib/ruby_llm/providers/gpustack/embeddings.rb +2 -2
- data/lib/ruby_llm/providers/gpustack/media.rb +8 -8
- data/lib/ruby_llm/providers/gpustack/transcription.rb +14 -6
- data/lib/ruby_llm/providers/gpustack.rb +1 -1
- data/lib/ruby_llm/providers/hetzner/chat.rb +23 -6
- data/lib/ruby_llm/providers/hetzner.rb +5 -1
- data/lib/ruby_llm/providers/meta/responses.rb +81 -0
- data/lib/ruby_llm/providers/meta.rb +37 -0
- data/lib/ruby_llm/providers/minimax/anthropic.rb +95 -0
- data/lib/ruby_llm/providers/minimax/chat_completions.rb +48 -0
- data/lib/ruby_llm/providers/minimax/responses.rb +52 -0
- data/lib/ruby_llm/providers/minimax.rb +104 -0
- data/lib/ruby_llm/providers/mistral/chat.rb +34 -4
- data/lib/ruby_llm/providers/mistral/chat_completions/batches.rb +8 -3
- data/lib/ruby_llm/providers/mistral/embeddings.rb +1 -12
- data/lib/ruby_llm/providers/mistral/input_tokens.rb +35 -0
- data/lib/ruby_llm/providers/mistral/media.rb +8 -8
- data/lib/ruby_llm/providers/mistral/transcription.rb +8 -6
- data/lib/ruby_llm/providers/mistral.rb +13 -1
- data/lib/ruby_llm/providers/moonshot/capabilities.rb +16 -0
- data/lib/ruby_llm/providers/moonshot/chat.rb +100 -0
- data/lib/ruby_llm/providers/moonshot/documents.rb +79 -0
- data/lib/ruby_llm/providers/moonshot/responses.rb +55 -0
- data/lib/ruby_llm/providers/moonshot.rb +54 -0
- data/lib/ruby_llm/providers/ollama/chat.rb +5 -6
- data/lib/ruby_llm/providers/ollama/embeddings.rb +41 -0
- data/lib/ruby_llm/providers/ollama/media.rb +6 -6
- data/lib/ruby_llm/providers/ollama/speech.rb +24 -0
- data/lib/ruby_llm/providers/ollama.rb +13 -1
- data/lib/ruby_llm/providers/ollama_cloud.rb +22 -1
- data/lib/ruby_llm/providers/openai/responses.rb +8 -0
- data/lib/ruby_llm/providers/openai.rb +7 -3
- data/lib/ruby_llm/providers/openrouter/chat.rb +15 -9
- data/lib/ruby_llm/providers/openrouter/embeddings.rb +12 -8
- data/lib/ruby_llm/providers/openrouter/images.rb +16 -10
- data/lib/ruby_llm/providers/openrouter/judgments.rb +47 -0
- data/lib/ruby_llm/providers/openrouter/media.rb +5 -5
- data/lib/ruby_llm/providers/openrouter/models.rb +19 -7
- data/lib/ruby_llm/providers/openrouter/streaming.rb +4 -4
- data/lib/ruby_llm/providers/openrouter/videos.rb +1 -1
- data/lib/ruby_llm/providers/openrouter.rb +32 -5
- data/lib/ruby_llm/providers/perplexity/chat.rb +3 -3
- data/lib/ruby_llm/providers/perplexity/embeddings.rb +1 -1
- data/lib/ruby_llm/providers/perplexity/media.rb +6 -6
- data/lib/ruby_llm/providers/perplexity.rb +13 -2
- data/lib/ruby_llm/providers/typesafe.rb +1 -1
- data/lib/ruby_llm/providers/vertexai/anthropic.rb +5 -1
- data/lib/ruby_llm/providers/vertexai/credentials.rb +27 -3
- data/lib/ruby_llm/providers/vertexai/embed_content.rb +1 -1
- data/lib/ruby_llm/providers/vertexai/embeddings.rb +2 -3
- data/lib/ruby_llm/providers/vertexai/gemini.rb +1 -1
- data/lib/ruby_llm/providers/vertexai/mistral.rb +5 -0
- data/lib/ruby_llm/providers/vertexai/models.rb +24 -2
- data/lib/ruby_llm/providers/vertexai/transcription.rb +2 -2
- data/lib/ruby_llm/providers/vertexai/videos.rb +1 -1
- data/lib/ruby_llm/providers/vertexai.rb +56 -9
- data/lib/ruby_llm/providers/xai/chat.rb +3 -3
- data/lib/ruby_llm/providers/xai/chat_completions/batches.rb +8 -3
- data/lib/ruby_llm/providers/xai/images.rb +9 -4
- data/lib/ruby_llm/providers/xai/models.rb +2 -2
- data/lib/ruby_llm/providers/xai/responses.rb +10 -0
- data/lib/ruby_llm/providers/xai/speech.rb +2 -2
- data/lib/ruby_llm/providers/xai/transcription.rb +14 -9
- data/lib/ruby_llm/providers/xai/videos.rb +1 -1
- data/lib/ruby_llm/providers/xai.rb +9 -1
- data/lib/ruby_llm/providers/zai/chat.rb +117 -0
- data/lib/ruby_llm/providers/zai/ocr.rb +65 -0
- data/lib/ruby_llm/providers/zai/token_counting.rb +25 -0
- data/lib/ruby_llm/providers/zai/videos.rb +67 -0
- data/lib/ruby_llm/providers/zai.rb +66 -0
- data/lib/ruby_llm/question.rb +390 -0
- data/lib/ruby_llm/queued_message.rb +74 -0
- data/lib/ruby_llm/railtie.rb +43 -0
- data/lib/ruby_llm/request_shape/part.rb +2 -2
- data/lib/ruby_llm/request_shape/problem.rb +2 -2
- data/lib/ruby_llm/request_shape/tool_round.rb +2 -2
- data/lib/ruby_llm/request_shape/turn.rb +2 -2
- data/lib/ruby_llm/request_shape.rb +1 -1
- data/lib/ruby_llm/rerank.rb +10 -37
- data/lib/ruby_llm/research_job.rb +26 -13
- data/lib/ruby_llm/sandbox/landlock.rb +78 -0
- data/lib/ruby_llm/sandbox/mxc.rb +137 -0
- data/lib/ruby_llm/sandbox.rb +141 -0
- data/lib/ruby_llm/search_results.rb +9 -4
- data/lib/ruby_llm/server_tool_call.rb +1 -1
- data/lib/ruby_llm/skill/loader.rb +131 -0
- data/lib/ruby_llm/skill.rb +263 -0
- data/lib/ruby_llm/speech.rb +25 -53
- data/lib/ruby_llm/stored_file.rb +94 -0
- data/lib/ruby_llm/support/cancellation.rb +4 -4
- data/lib/ruby_llm/support/deprecated_enumerable.rb +13 -0
- data/lib/ruby_llm/support/deprecated_job_predicates.rb +24 -0
- data/lib/ruby_llm/support/inspectable.rb +1 -1
- data/lib/ruby_llm/support/instrumentation.rb +6 -5
- data/lib/ruby_llm/support/rescuable.rb +84 -0
- data/lib/ruby_llm/support/utils.rb +26 -3
- data/lib/ruby_llm/task.rb +126 -0
- data/lib/ruby_llm/test_helper.rb +129 -0
- data/lib/ruby_llm/thinking.rb +3 -3
- data/lib/ruby_llm/tokens.rb +52 -12
- data/lib/ruby_llm/tool.rb +339 -5
- data/lib/ruby_llm/tool_call/collection.rb +132 -0
- data/lib/ruby_llm/tool_call.rb +68 -6
- data/lib/ruby_llm/tools/provider_tools.rb +23 -0
- data/lib/ruby_llm/tools/search.rb +118 -0
- data/lib/ruby_llm/transcription/segment.rb +67 -0
- data/lib/ruby_llm/transcription/wav_audio.rb +2 -1
- data/lib/ruby_llm/transcription.rb +25 -49
- data/lib/ruby_llm/transcription_chunk.rb +9 -9
- data/lib/ruby_llm/transport/connection.rb +146 -33
- data/lib/ruby_llm/transport/error_middleware.rb +94 -49
- data/lib/ruby_llm/transport/event_stream_parser.rb +18 -4
- data/lib/ruby_llm/transport/json_response.rb +9 -3
- data/lib/ruby_llm/transport/log_formatter.rb +20 -0
- data/lib/ruby_llm/transport/release_request.rb +35 -0
- data/lib/ruby_llm/transport/websocket_connection.rb +26 -30
- data/lib/ruby_llm/uploaded_file.rb +15 -6
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm/video.rb +34 -10
- data/lib/ruby_llm/video_job.rb +55 -42
- data/lib/ruby_llm/web_source.rb +38 -0
- data/lib/ruby_llm/workflow.rb +2 -2
- data/lib/ruby_llm.rb +63 -25
- data/lib/tasks/ruby_llm/evaluations.rb +1 -1
- data/skills/rubyllm/SKILL.md +128 -54
- data/skills/rubyllm/references/cost-and-tracing.md +8 -0
- data/skills/rubyllm/references/files-code-and-context.md +6 -0
- data/skills/rubyllm/references/operations.md +8 -0
- data/skills/rubyllm/references/rails-chats.md +22 -0
- data/skills/rubyllm/references/tools-and-agents.md +11 -0
- metadata +219 -14
- data/lib/generators/ruby_llm/chat_ui/templates/helpers/messages_helper.rb.tt +0 -25
- data/lib/generators/ruby_llm/upgrade/templates/upgrade_ruby_llm_to_2_1.rb.tt +0 -78
data/lib/ruby_llm/provider.rb
CHANGED
|
@@ -82,7 +82,7 @@ module RubyLLM
|
|
|
82
82
|
# retrying a rate-limited request, or +nil+ when the response carries
|
|
83
83
|
# no timing information. The retry middleware already honors the
|
|
84
84
|
# standard <tt>Retry-After</tt> header; override this to read
|
|
85
|
-
# provider-specific rate-limit headers.
|
|
85
|
+
# provider-specific rate-limit headers or the error body.
|
|
86
86
|
#
|
|
87
87
|
# def retry_delay(response)
|
|
88
88
|
# response.response_headers['x-acme-ratelimit-reset']&.to_f
|
|
@@ -111,6 +111,10 @@ module RubyLLM
|
|
|
111
111
|
self.class.configuration_requirements
|
|
112
112
|
end
|
|
113
113
|
|
|
114
|
+
def same_credentials?(other) # :nodoc:
|
|
115
|
+
settings == other.settings
|
|
116
|
+
end
|
|
117
|
+
|
|
114
118
|
def original_image_detail? # :nodoc:
|
|
115
119
|
false
|
|
116
120
|
end
|
|
@@ -135,9 +139,10 @@ module RubyLLM
|
|
|
135
139
|
|
|
136
140
|
def complete(messages, tools:, temperature:, model:, provider_options: {}, headers: {}, schema: nil, # :nodoc:
|
|
137
141
|
max_output_tokens: nil, thinking: nil, citations: false, caching: nil, tool_prefs: nil,
|
|
138
|
-
protocol: nil, before_request: [], usage_recorder: nil,
|
|
139
|
-
compaction: nil, end_user: nil, &)
|
|
140
|
-
protocol_class = resolve_protocol(protocol, model, tools:, schema:, thinking:, tool_prefs:, citations
|
|
142
|
+
protocol: nil, before_request: [], usage_recorder: nil, progress_recorder: nil,
|
|
143
|
+
provider_tools: [], compaction: nil, end_user: nil, &)
|
|
144
|
+
protocol_class = resolve_protocol(protocol, model, tools:, schema:, thinking:, tool_prefs:, citations:,
|
|
145
|
+
provider_tools:, messages:)
|
|
141
146
|
protocol_class.new(self, model).complete(
|
|
142
147
|
messages,
|
|
143
148
|
tools: tools,
|
|
@@ -155,12 +160,21 @@ module RubyLLM
|
|
|
155
160
|
end_user: end_user,
|
|
156
161
|
before_request: before_request,
|
|
157
162
|
usage_recorder: usage_recorder,
|
|
163
|
+
progress_recorder: progress_recorder,
|
|
158
164
|
&
|
|
159
165
|
)
|
|
160
166
|
end
|
|
161
167
|
|
|
162
|
-
def
|
|
163
|
-
|
|
168
|
+
def tool_search?(model, protocol: nil) # :nodoc:
|
|
169
|
+
preprocessing_protocol(protocol, model).tool_search?
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def skipped_tool_call_error(tool_call, model:, protocol: nil) # :nodoc:
|
|
173
|
+
resolve_protocol(protocol, model).new(self, model).skipped_tool_call_error(tool_call)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def tool_approval_response(tool_call, approved:, model:, protocol: nil, note: nil) # :nodoc:
|
|
177
|
+
resolve_protocol(protocol, model).new(self, model).tool_approval_response(tool_call, approved:, note:)
|
|
164
178
|
end
|
|
165
179
|
|
|
166
180
|
def compact(messages, model:, protocol: nil, headers: {}, before_request: [], usage_recorder: nil) # :nodoc:
|
|
@@ -172,7 +186,8 @@ module RubyLLM
|
|
|
172
186
|
def render(messages, tools:, temperature:, model:, provider_options: {}, schema: nil, thinking: nil, # :nodoc:
|
|
173
187
|
max_output_tokens: nil, citations: false, caching: nil, tool_prefs: nil, protocol: nil,
|
|
174
188
|
before_request: [], provider_tools: [], compaction: nil, end_user: nil)
|
|
175
|
-
protocol_class = resolve_protocol(protocol, model, tools:, schema:, thinking:, tool_prefs:, citations
|
|
189
|
+
protocol_class = resolve_protocol(protocol, model, tools:, schema:, thinking:, tool_prefs:, citations:,
|
|
190
|
+
provider_tools:, messages:)
|
|
176
191
|
protocol_class.new(self, model).render(
|
|
177
192
|
messages,
|
|
178
193
|
tools: tools,
|
|
@@ -205,6 +220,14 @@ module RubyLLM
|
|
|
205
220
|
)
|
|
206
221
|
end
|
|
207
222
|
|
|
223
|
+
def fallback_caching(caching, model:, protocol: nil) # :nodoc:
|
|
224
|
+
preprocessing_protocol(protocol, model).fallback_caching(caching)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def cache_key?(model:, protocol: nil) # :nodoc:
|
|
228
|
+
preprocessing_protocol(protocol, model).cache_key?
|
|
229
|
+
end
|
|
230
|
+
|
|
208
231
|
def preprocess_messages(messages, model:, protocol: nil, unsupported_attachment: nil) # :nodoc:
|
|
209
232
|
preprocessor = preprocessing_protocol(protocol, model)
|
|
210
233
|
messages.map { |message| preprocessor.preprocess_message(message, unsupported_attachment:) }
|
|
@@ -251,7 +274,7 @@ module RubyLLM
|
|
|
251
274
|
|
|
252
275
|
def batch_cost(tokens, model:, category: :text_tokens) # :nodoc:
|
|
253
276
|
standard = Cost.new(tokens:, model:, category:)
|
|
254
|
-
return standard if tokens.reported_cost
|
|
277
|
+
return standard if tokens.reported_cost || model.nil?
|
|
255
278
|
|
|
256
279
|
pricing = model.pricing.public_send(category)
|
|
257
280
|
batch_tier = pricing.batch unless long_context_pricing?(pricing, tokens)
|
|
@@ -320,10 +343,9 @@ module RubyLLM
|
|
|
320
343
|
end
|
|
321
344
|
|
|
322
345
|
def embed(text, model:, dimensions:, task_type: nil, title: nil, with: nil, provider_options: {}) # :nodoc:
|
|
323
|
-
protocol = resolve_protocol(nil, model, operation: :embed)
|
|
324
|
-
protocol.
|
|
325
|
-
|
|
326
|
-
)
|
|
346
|
+
protocol = resolve_protocol(nil, model, operation: :embed).new(self, model)
|
|
347
|
+
protocol.reject_options(:embed, task_type:, title:)
|
|
348
|
+
protocol.embed(text, model: model_id_for(model), dimensions:, task_type:, title:, with:, provider_options:)
|
|
327
349
|
end
|
|
328
350
|
|
|
329
351
|
def render_embedding(text, model:, dimensions: nil) # :nodoc:
|
|
@@ -347,10 +369,9 @@ module RubyLLM
|
|
|
347
369
|
end
|
|
348
370
|
|
|
349
371
|
def paint(prompt, model:, size:, count: nil, with: nil, mask: nil, provider_options: {}) # :nodoc:
|
|
350
|
-
protocol = resolve_protocol(nil, model, operation: :paint)
|
|
351
|
-
protocol.
|
|
352
|
-
|
|
353
|
-
)
|
|
372
|
+
protocol = resolve_protocol(nil, model, operation: :paint).new(self, model)
|
|
373
|
+
protocol.reject_options(:paint, count: (count if count.to_i > 1))
|
|
374
|
+
protocol.paint(prompt, model: model_id_for(model), size:, count:, with:, mask:, provider_options:)
|
|
354
375
|
end
|
|
355
376
|
|
|
356
377
|
def animate_later(prompt, model:, with: nil, extend: nil, provider_options: {}) # :nodoc:
|
|
@@ -360,6 +381,11 @@ module RubyLLM
|
|
|
360
381
|
)
|
|
361
382
|
end
|
|
362
383
|
|
|
384
|
+
def find_video_job(id, model:) # :nodoc:
|
|
385
|
+
protocol = resolve_protocol(nil, model, operation: :animate)
|
|
386
|
+
protocol.new(self, model).find_video_job(id, model: model_id_for(model))
|
|
387
|
+
end
|
|
388
|
+
|
|
363
389
|
def speak(input, model:, voice:, format:, provider_options: {}, &) # :nodoc:
|
|
364
390
|
protocol = resolve_protocol(nil, model, operation: :speak)
|
|
365
391
|
protocol.new(self, model).speak(
|
|
@@ -370,6 +396,8 @@ module RubyLLM
|
|
|
370
396
|
def transcribe(audio_file, model:, language:, format: nil, timestamps: nil, speaker_names: nil, # :nodoc:
|
|
371
397
|
speaker_references: nil, provider_options: {}, prompt: nil, temperature: nil, &)
|
|
372
398
|
protocol = resolve_protocol(nil, model, operation: :transcribe).new(self, model)
|
|
399
|
+
protocol.reject_options(:transcribe, format:, temperature:, prompt:, speaker_names:, speaker_references:,
|
|
400
|
+
named_speakers: (speaker_names unless Array(speaker_names).empty?))
|
|
373
401
|
options = protocol.render_transcription_options(timestamps:, format:, streaming: block_given?)
|
|
374
402
|
provider_options = Support::Utils.deep_merge(options, provider_options)
|
|
375
403
|
protocol.transcribe(
|
|
@@ -406,7 +434,9 @@ module RubyLLM
|
|
|
406
434
|
ensure_files_supported!
|
|
407
435
|
options = { filename:, purpose:, expires_in:, uri:, content_type:, provider_options: }.compact
|
|
408
436
|
|
|
409
|
-
protocols.fetch(:files).new(self)
|
|
437
|
+
protocol = protocols.fetch(:files).new(self)
|
|
438
|
+
protocol.reject_options(:upload, purpose:, expires_in:, uri:)
|
|
439
|
+
protocol.upload(file, **options)
|
|
410
440
|
end
|
|
411
441
|
|
|
412
442
|
def cache_content(content, model:, ttl: nil, instructions: nil, with: nil) # :nodoc:
|
|
@@ -443,6 +473,10 @@ module RubyLLM
|
|
|
443
473
|
protocols.fetch(:files).new(self).list_uris(uri)
|
|
444
474
|
end
|
|
445
475
|
|
|
476
|
+
def settings # :nodoc:
|
|
477
|
+
self.class.configuration_options.map { |option| config.public_send(option) }
|
|
478
|
+
end
|
|
479
|
+
|
|
446
480
|
def configured? # :nodoc:
|
|
447
481
|
self.class.configured?(@config)
|
|
448
482
|
end
|
|
@@ -470,6 +504,28 @@ module RubyLLM
|
|
|
470
504
|
end
|
|
471
505
|
end
|
|
472
506
|
|
|
507
|
+
# Returns the status a response sent as a success really means, for a
|
|
508
|
+
# provider that reports failures in a 2xx body, or +nil+.
|
|
509
|
+
def error_status(_response) # :nodoc:
|
|
510
|
+
nil
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
# Returns the provider's code for an error +response+, such as
|
|
514
|
+
# <tt>"invalid_request_error"</tt>, from the body's +code+, +status+,
|
|
515
|
+
# or +type+ under +error+, or its +code+ or +type+, or +nil+. Override
|
|
516
|
+
# when the provider names its errors elsewhere.
|
|
517
|
+
def parse_error_code(response) # :nodoc:
|
|
518
|
+
body = parse_error_body(response)
|
|
519
|
+
body = body.find { |part| part.is_a?(Hash) } if body.is_a?(Array)
|
|
520
|
+
return unless body.is_a?(Hash)
|
|
521
|
+
|
|
522
|
+
error = body['error']
|
|
523
|
+
candidates = error.is_a?(Hash) ? error.values_at('code', 'status', 'type') : []
|
|
524
|
+
candidates << body['code']
|
|
525
|
+
candidates << body['type'] unless body['type'] == 'error'
|
|
526
|
+
candidates.find { |candidate| candidate.is_a?(String) && candidate.match?(Error::ERROR_CODE) }
|
|
527
|
+
end
|
|
528
|
+
|
|
473
529
|
class << self
|
|
474
530
|
attr_reader :default_protocol # :nodoc:
|
|
475
531
|
attr_writer :slug # :nodoc:
|
|
@@ -517,6 +573,18 @@ module RubyLLM
|
|
|
517
573
|
id
|
|
518
574
|
end
|
|
519
575
|
|
|
576
|
+
# The models.dev provider ids whose catalogs list this provider's
|
|
577
|
+
# models. Empty for a provider models.dev does not list.
|
|
578
|
+
def models_dev_provider_ids # :nodoc:
|
|
579
|
+
[]
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
# The provider's +gen_ai.provider.name+ in the OpenTelemetry GenAI
|
|
583
|
+
# conventions. Providers the conventions name differently override this.
|
|
584
|
+
def open_telemetry_name # :nodoc:
|
|
585
|
+
slug
|
|
586
|
+
end
|
|
587
|
+
|
|
520
588
|
# Returns the configuration keys that must be set before the
|
|
521
589
|
# provider is usable. The base implementation returns an empty
|
|
522
590
|
# array.
|
|
@@ -560,7 +628,7 @@ module RubyLLM
|
|
|
560
628
|
|
|
561
629
|
# Returns whether +operation+ requires an inference model. Override
|
|
562
630
|
# for endpoints that operate on an explicitly configured resource.
|
|
563
|
-
def model_required?(**)
|
|
631
|
+
def model_required?(**) # :nodoc:
|
|
564
632
|
true
|
|
565
633
|
end
|
|
566
634
|
|
|
@@ -584,6 +652,25 @@ module RubyLLM
|
|
|
584
652
|
@protocols ||= {}
|
|
585
653
|
end
|
|
586
654
|
|
|
655
|
+
# Returns the part of +message+ that holds its answer, as read by the
|
|
656
|
+
# first of the provider's protocols that marks parts of a turn as
|
|
657
|
+
# intermediate, or +nil+ when none does. Such a protocol defines
|
|
658
|
+
# +parse_answer_text(message)+ as a class method.
|
|
659
|
+
def answer_text(message) # :nodoc:
|
|
660
|
+
protocols.each_value.lazy.filter_map do |protocol|
|
|
661
|
+
protocol.parse_answer_text(message) if protocol.respond_to?(:parse_answer_text)
|
|
662
|
+
end.first
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
# Returns the pages hosted web searches used for +message+, as read by
|
|
666
|
+
# each of the provider's protocols that reports them. Such a protocol
|
|
667
|
+
# defines +parse_web_sources(message)+ as a class method.
|
|
668
|
+
def web_sources(message) # :nodoc:
|
|
669
|
+
protocols.each_value.flat_map do |protocol|
|
|
670
|
+
protocol.respond_to?(:parse_web_sources) ? protocol.parse_web_sources(message) : []
|
|
671
|
+
end
|
|
672
|
+
end
|
|
673
|
+
|
|
587
674
|
# Registers +provider_class+ under the slug +name+, making it
|
|
588
675
|
# available to RubyLLM.chat and the other top-level helpers.
|
|
589
676
|
# Stamps the class's slug, adds it to ::providers, and defines a
|
|
@@ -613,14 +700,14 @@ module RubyLLM
|
|
|
613
700
|
# Resolves +model_id+ to the id the registry stores it under for this
|
|
614
701
|
# provider. Defaults to the id unchanged; providers whose catalog ids
|
|
615
702
|
# differ from their request ids (Bedrock's region prefixes) override it.
|
|
616
|
-
def resolve_registry_id(model_id, _models, _config = nil)
|
|
703
|
+
def resolve_registry_id(model_id, _models, _config = nil) # :nodoc:
|
|
617
704
|
model_id
|
|
618
705
|
end
|
|
619
706
|
|
|
620
707
|
# Returns the id of the model that the deployment named +model_id+
|
|
621
708
|
# runs, or +nil+ when +model_id+ is not a declared deployment.
|
|
622
709
|
# Providers whose requests name deployments override it.
|
|
623
|
-
def deployed_model_id(_model_id, _config = nil)
|
|
710
|
+
def deployed_model_id(_model_id, _config = nil) # :nodoc:
|
|
624
711
|
nil
|
|
625
712
|
end
|
|
626
713
|
|
|
@@ -634,14 +721,6 @@ module RubyLLM
|
|
|
634
721
|
@model_registry_files ||= {}
|
|
635
722
|
end
|
|
636
723
|
|
|
637
|
-
def local_providers # :nodoc:
|
|
638
|
-
providers.select { |_slug, provider_class| provider_class.local? }
|
|
639
|
-
end
|
|
640
|
-
|
|
641
|
-
def remote_providers # :nodoc:
|
|
642
|
-
providers.select { |_slug, provider_class| provider_class.remote? }
|
|
643
|
-
end
|
|
644
|
-
|
|
645
724
|
def configured_providers(config) # :nodoc:
|
|
646
725
|
providers.select do |_slug, provider_class|
|
|
647
726
|
provider_class.configured?(config)
|
|
@@ -711,6 +790,11 @@ module RubyLLM
|
|
|
711
790
|
end
|
|
712
791
|
|
|
713
792
|
def fetch_protocol(name)
|
|
793
|
+
if protocols.empty?
|
|
794
|
+
raise Error, "#{self.class.display_name} has no protocols. A provider subclass does not inherit its " \
|
|
795
|
+
'parent\'s protocols, so declare them with `protocol`.'
|
|
796
|
+
end
|
|
797
|
+
|
|
714
798
|
protocols.fetch(name.to_sym) do
|
|
715
799
|
raise Error, "#{name} is not a protocol of #{self.class.display_name}. Available: #{protocols.keys.join(', ')}"
|
|
716
800
|
end
|
|
@@ -720,6 +804,19 @@ module RubyLLM
|
|
|
720
804
|
model.respond_to?(:id) ? model.id : model
|
|
721
805
|
end
|
|
722
806
|
|
|
807
|
+
# Returns the base URL +setting+ holds, or +default+, as given. Raises
|
|
808
|
+
# when it has no http(s) scheme or ends in one of the provider's
|
|
809
|
+
# +endpoints+, since RubyLLM adds those paths itself.
|
|
810
|
+
def configured_api_base(setting, default = nil, example: default, endpoints: nil)
|
|
811
|
+
value = @config.public_send(setting) || default
|
|
812
|
+
endpoint = value.to_s.sub(/[?#].*/, '').chomp('/')[endpoints] if endpoints
|
|
813
|
+
return value if value.to_s.match?(%r{\Ahttps?://[^/?#]}i) && !endpoint
|
|
814
|
+
|
|
815
|
+
raise ConfigurationError, "#{setting} should look like #{example}" \
|
|
816
|
+
"#{endpoint ? " (without #{endpoint})" : ', starting with http:// or https://'}, " \
|
|
817
|
+
"got #{value.inspect}"
|
|
818
|
+
end
|
|
819
|
+
|
|
723
820
|
def account_digest(*parts)
|
|
724
821
|
Digest::SHA256.hexdigest(parts.join("\0"))
|
|
725
822
|
end
|
|
@@ -745,9 +842,9 @@ module RubyLLM
|
|
|
745
842
|
error = part['error']
|
|
746
843
|
return error if error.is_a?(String)
|
|
747
844
|
|
|
748
|
-
|
|
749
|
-
[
|
|
750
|
-
|
|
845
|
+
detail = part['detail']
|
|
846
|
+
messages = [error, part, detail].filter_map { |source| source['message'] if source.is_a?(Hash) }
|
|
847
|
+
[*messages, detail].find { |message| message.is_a?(String) } || validation_detail_message(detail)
|
|
751
848
|
end
|
|
752
849
|
|
|
753
850
|
def validation_detail_message(detail)
|
|
@@ -13,7 +13,7 @@ module RubyLLM
|
|
|
13
13
|
protocol :files, Protocols::Anthropic::Files
|
|
14
14
|
|
|
15
15
|
def api_base
|
|
16
|
-
|
|
16
|
+
configured_api_base(:anthropic_api_base, 'https://api.anthropic.com', endpoints: %r{/v1/messages\z})
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def headers
|
|
@@ -30,6 +30,10 @@ module RubyLLM
|
|
|
30
30
|
def batch_cost_multiplier(**) = 0.5
|
|
31
31
|
|
|
32
32
|
class << self
|
|
33
|
+
def models_dev_provider_ids # :nodoc:
|
|
34
|
+
%w[anthropic]
|
|
35
|
+
end
|
|
36
|
+
|
|
33
37
|
def capabilities
|
|
34
38
|
Anthropic::Capabilities
|
|
35
39
|
end
|
|
@@ -13,13 +13,27 @@ module RubyLLM
|
|
|
13
13
|
Azure::Media.supported_attachment?(attachment)
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def
|
|
17
|
-
Media.
|
|
16
|
+
def render_content(content, attachments = [])
|
|
17
|
+
Media.render_content(content, attachments)
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
def
|
|
20
|
+
def render_role(role)
|
|
21
21
|
role.to_s
|
|
22
22
|
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
# Grok on Azure reports cached tokens beside prompt_tokens, not inside
|
|
27
|
+
# them, so prompt_tokens is the uncached input.
|
|
28
|
+
def input_tokens(usage)
|
|
29
|
+
grok? ? usage['prompt_tokens'] : super
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def grok?
|
|
33
|
+
return false unless @model
|
|
34
|
+
|
|
35
|
+
@model.family == 'grok' || @model.id.start_with?('grok')
|
|
36
|
+
end
|
|
23
37
|
end
|
|
24
38
|
end
|
|
25
39
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Providers
|
|
5
|
+
class Azure < Provider
|
|
6
|
+
# Azure's dialect of OpenAI Decisions, served from the openai/v1 base.
|
|
7
|
+
# The model field carries the deployment name, as on Responses. The v1
|
|
8
|
+
# routes accept only the v1 and preview API versions, so the request
|
|
9
|
+
# names v1 over any dated version the configured base carries.
|
|
10
|
+
class Decisions < Protocols::OpenAI::Decisions
|
|
11
|
+
def judgment_url
|
|
12
|
+
"#{@provider.azure_openai_v1_base.sub(%r{/+\z}, '')}/decisions?api-version=v1"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -11,8 +11,7 @@ module RubyLLM
|
|
|
11
11
|
azure_endpoint(:embeddings)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
def render_embedding_payload(text, model:, dimensions:, task_type: nil, title: nil, provider_options: {})
|
|
14
|
+
def render_embedding_payload(text, model:, dimensions:, provider_options: {}, **)
|
|
16
15
|
{
|
|
17
16
|
model: model,
|
|
18
17
|
input: [text].flatten,
|
|
@@ -11,23 +11,23 @@ module RubyLLM
|
|
|
11
11
|
%i[image audio text].include?(attachment.type)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def
|
|
15
|
-
Protocols::ChatCompletions::Media.
|
|
14
|
+
def render_content(content, attachments = [])
|
|
15
|
+
Protocols::ChatCompletions::Media.render_parts(content, attachments) do |attachment|
|
|
16
16
|
raise UnsupportedAttachmentError, attachment.mime_type unless supported_attachment?(attachment)
|
|
17
17
|
|
|
18
18
|
case attachment.type
|
|
19
19
|
when :image
|
|
20
|
-
Protocols::ChatCompletions::Media.with_image_detail(
|
|
20
|
+
Protocols::ChatCompletions::Media.with_image_detail(render_image(attachment), attachment,
|
|
21
21
|
original_detail: true)
|
|
22
22
|
when :audio
|
|
23
|
-
Protocols::ChatCompletions::Media.
|
|
23
|
+
Protocols::ChatCompletions::Media.render_audio(attachment)
|
|
24
24
|
when :text
|
|
25
|
-
Protocols::ChatCompletions::Media.
|
|
25
|
+
Protocols::ChatCompletions::Media.render_text_file(attachment)
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
def
|
|
30
|
+
def render_image(image)
|
|
31
31
|
{
|
|
32
32
|
type: 'image_url',
|
|
33
33
|
image_url: {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Providers
|
|
5
|
+
class Azure < Provider
|
|
6
|
+
# Azure's Chat Completions plus Mistral's document models on Foundry,
|
|
7
|
+
# which read Mistral's OCR request at a route under the resource root.
|
|
8
|
+
class Mistral < Azure::ChatCompletions
|
|
9
|
+
include Providers::Mistral::OCR
|
|
10
|
+
|
|
11
|
+
def ocr_url
|
|
12
|
+
"#{@provider.azure_base_parts[:root]}/providers/mistral/azure/ocr"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -10,12 +10,26 @@ module RubyLLM
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
# Azure reports the same id once per region it is offered in, and keeps
|
|
13
|
-
# returning models whose inference deprecation date has passed.
|
|
13
|
+
# returning models whose inference deprecation date has passed. The
|
|
14
|
+
# listing leaves out Azure AI Speech, so fast transcription is
|
|
15
|
+
# appended.
|
|
14
16
|
def parse_list_models_response(response, slug)
|
|
15
17
|
Array(response.body['data'])
|
|
16
18
|
.uniq { |model_data| model_data['id'] }
|
|
17
19
|
.reject { |model_data| azure_inference_retired?(model_data) }
|
|
18
|
-
.map { |model_data| azure_model(model_data, slug) }
|
|
20
|
+
.map { |model_data| azure_model(model_data, slug) } + [fast_transcription_model(slug)]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def fast_transcription_model(slug)
|
|
24
|
+
Model.new(
|
|
25
|
+
id: Azure::FAST_TRANSCRIPTION_MODEL,
|
|
26
|
+
name: 'Fast Transcription',
|
|
27
|
+
provider: slug,
|
|
28
|
+
family: 'azure-speech',
|
|
29
|
+
modalities: { input: ['audio'], output: ['text'] },
|
|
30
|
+
capabilities: ['transcription'],
|
|
31
|
+
pricing: {}
|
|
32
|
+
)
|
|
19
33
|
end
|
|
20
34
|
|
|
21
35
|
def azure_inference_retired?(model_data)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Providers
|
|
5
|
+
class Azure
|
|
6
|
+
# Foundry's REST reference lists /openai/v1/moderations, but resource
|
|
7
|
+
# and project endpoints answer it with the 404 of an unknown route and
|
|
8
|
+
# the catalog holds no moderation model.
|
|
9
|
+
module Moderation
|
|
10
|
+
def render_moderation_payload(*, **)
|
|
11
|
+
raise NotImplementedError
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -11,12 +11,21 @@ module RubyLLM
|
|
|
11
11
|
include Azure::Videos
|
|
12
12
|
include Azure::Images
|
|
13
13
|
include Azure::Audio
|
|
14
|
+
include Azure::Moderation
|
|
14
15
|
include Protocols::Responses::Compaction
|
|
15
16
|
|
|
16
17
|
def completion_url
|
|
17
18
|
"#{@provider.azure_openai_v1_base}/responses"
|
|
18
19
|
end
|
|
19
20
|
|
|
21
|
+
def document_urls?
|
|
22
|
+
true
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def web_search_sources?
|
|
26
|
+
true
|
|
27
|
+
end
|
|
28
|
+
|
|
20
29
|
def provider_file_upload_options(_attachment)
|
|
21
30
|
{ purpose: 'assistants' }
|
|
22
31
|
end
|
|
@@ -13,7 +13,7 @@ module RubyLLM
|
|
|
13
13
|
azure_video_url('video/generations/jobs')
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def render_video_payload(prompt, model:,
|
|
16
|
+
def render_video_payload(prompt, model:, provider_options: {}, **)
|
|
17
17
|
{ model: model, prompt: prompt }.merge(provider_options)
|
|
18
18
|
end
|
|
19
19
|
|
|
@@ -33,7 +33,7 @@ module RubyLLM
|
|
|
33
33
|
azure_video_url("video/generations/jobs/#{job.id}")
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
def parse_video_job_status(response,
|
|
36
|
+
def parse_video_job_status(response, **)
|
|
37
37
|
body = response.body
|
|
38
38
|
case body['status']
|
|
39
39
|
when 'succeeded' then { status: :completed, raw: body }
|
|
@@ -7,18 +7,30 @@ module RubyLLM
|
|
|
7
7
|
DEFAULT_CHAT_API_VERSION = '2024-05-01-preview'
|
|
8
8
|
DEFAULT_EMBEDDINGS_API_VERSION = '2024-05-01-preview'
|
|
9
9
|
DEFAULT_MODELS_API_VERSION = 'preview'
|
|
10
|
+
SPEECH_API_VERSION = '2025-10-15'
|
|
11
|
+
|
|
12
|
+
# Azure AI Speech fast transcription. The endpoint takes no model, so
|
|
13
|
+
# RubyLLM names it after the API.
|
|
14
|
+
FAST_TRANSCRIPTION_MODEL = 'fast-transcription'
|
|
10
15
|
|
|
11
16
|
protocol :chat_completions, Azure::ChatCompletions, batches: Azure::ChatCompletions::Batches
|
|
12
17
|
protocol :responses, Azure::Responses
|
|
13
18
|
protocol :files, Protocols::Azure::Files
|
|
14
19
|
protocol :cohere, Azure::Cohere
|
|
20
|
+
protocol :mistral, Azure::Mistral
|
|
21
|
+
protocol :decisions, Azure::Decisions
|
|
22
|
+
protocol :speech, Protocols::Azure::Speech
|
|
15
23
|
|
|
16
24
|
def original_image_detail? # :nodoc:
|
|
17
25
|
true
|
|
18
26
|
end
|
|
19
27
|
|
|
28
|
+
# A base ending in /models or /openai/v1/responses is the resource or
|
|
29
|
+
# its openai/v1 base, since RubyLLM adds those paths itself.
|
|
20
30
|
def api_base
|
|
21
|
-
|
|
31
|
+
configured_api_base(:azure_api_base, example: 'https://RESOURCE.openai.azure.com')
|
|
32
|
+
.sub(%r{\A(https?://[^/?#]+)/models/?(?=\?|\z)}, '\\1')
|
|
33
|
+
.sub(%r{/openai/v1/responses/?(?=\?|\z)}, '/openai/v1')
|
|
22
34
|
end
|
|
23
35
|
|
|
24
36
|
# Files belong to the Azure resource, whichever key or Entra ID token
|
|
@@ -27,12 +39,19 @@ module RubyLLM
|
|
|
27
39
|
account_digest(azure_openai_v1_base)
|
|
28
40
|
end
|
|
29
41
|
|
|
42
|
+
def resolve_protocol(name, model, operation: nil, **options)
|
|
43
|
+
name ||= :decisions if operation == :judge
|
|
44
|
+
name ||= :speech if operation == :transcribe && model&.id == FAST_TRANSCRIPTION_MODEL
|
|
45
|
+
super
|
|
46
|
+
end
|
|
47
|
+
|
|
30
48
|
# Deployments named after gpt-5.4+ and gpt-6+ models need the Responses
|
|
31
49
|
# API for tool use, so they route there automatically. Deployment names often
|
|
32
50
|
# differ from model ids, so the routing stays conservative; an explicit
|
|
33
51
|
# protocol: or the azure_protocol configuration option overrides it.
|
|
34
52
|
def protocol_for(model, operation: nil, **)
|
|
35
53
|
return protocols[:cohere] if operation == :rerank
|
|
54
|
+
return protocols[:mistral] if operation == :ocr
|
|
36
55
|
if operation == :embed &&
|
|
37
56
|
%w[Cohere-embed-v3-english Cohere-embed-v3-multilingual embed-v-4-0].include?(model.id)
|
|
38
57
|
return protocols[:cohere]
|
|
@@ -53,6 +72,21 @@ module RubyLLM
|
|
|
53
72
|
|
|
54
73
|
def batch_cost_multiplier(**) = 0.5
|
|
55
74
|
|
|
75
|
+
# Azure AI Speech answers on the resource's own host, whichever of its
|
|
76
|
+
# endpoints the API base names.
|
|
77
|
+
def azure_speech_transcription_url # :nodoc:
|
|
78
|
+
uri = URI.parse(api_base)
|
|
79
|
+
uri.path = '/speechtotext/transcriptions:transcribe'
|
|
80
|
+
uri.query = "api-version=#{SPEECH_API_VERSION}"
|
|
81
|
+
uri.to_s
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Azure AI Speech takes a resource key in its own header. An Entra ID
|
|
85
|
+
# token is already sent as a bearer token.
|
|
86
|
+
def azure_speech_headers # :nodoc:
|
|
87
|
+
@config.azure_api_key ? { 'Ocp-Apim-Subscription-Key' => @config.azure_api_key } : {}
|
|
88
|
+
end
|
|
89
|
+
|
|
56
90
|
def azure_cohere_url(operation) # :nodoc:
|
|
57
91
|
base = azure_base_parts[:path_base]
|
|
58
92
|
base = if base.include?('/providers/cohere')
|
|
@@ -113,6 +147,14 @@ module RubyLLM
|
|
|
113
147
|
end
|
|
114
148
|
|
|
115
149
|
class << self
|
|
150
|
+
def open_telemetry_name # :nodoc:
|
|
151
|
+
'azure.ai.openai'
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def models_dev_provider_ids # :nodoc:
|
|
155
|
+
%w[azure]
|
|
156
|
+
end
|
|
157
|
+
|
|
116
158
|
def capabilities
|
|
117
159
|
Azure::Capabilities
|
|
118
160
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Providers
|
|
5
|
+
class Bedrock
|
|
6
|
+
class Anthropic
|
|
7
|
+
# Bedrock Model Invocation Jobs whose records are InvokeModel bodies
|
|
8
|
+
# in the Anthropic Messages format.
|
|
9
|
+
module Batches
|
|
10
|
+
include Protocols::Bedrock::Batches
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def bedrock_invocation_type
|
|
15
|
+
'InvokeModel'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def validate_bedrock_batch_requests!(_requests); end
|
|
19
|
+
|
|
20
|
+
def bedrock_model_input(request)
|
|
21
|
+
payload = batch_payload(request)
|
|
22
|
+
render_invoke_body(payload, beta_headers(request[:headers].to_h, payload))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def parse_bedrock_model_output(body, model:)
|
|
26
|
+
parse_completion_body(body.merge('model' => model), raw: body)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|