ruby_llm 2.1.1 → 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 +160 -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 +515 -274
- 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 +1593 -331
- 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 +50 -24
- 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 +127 -106
- 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 +15 -15
- 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 +1 -7
- 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 +5 -14
- 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 +213 -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
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'base64'
|
|
4
|
+
|
|
5
|
+
module RubyLLM
|
|
6
|
+
module Providers
|
|
7
|
+
class Bedrock
|
|
8
|
+
# The Anthropic Messages API on Bedrock's InvokeModel endpoints, which
|
|
9
|
+
# serve every Claude model and cross-region inference profile. Unlike
|
|
10
|
+
# Converse, it carries Anthropic's own features, such as tool search,
|
|
11
|
+
# so a prompt cache survives the tools a model loads on demand.
|
|
12
|
+
class Anthropic < Protocols::Anthropic
|
|
13
|
+
include Protocols::Bedrock::EventStream
|
|
14
|
+
|
|
15
|
+
ANTHROPIC_VERSION = 'bedrock-2023-05-31'
|
|
16
|
+
TOOL_SEARCH_BETA = 'tool-search-tool-2025-10-19'
|
|
17
|
+
|
|
18
|
+
def completion_url
|
|
19
|
+
model_path(@model.id, 'invoke')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def stream_url
|
|
23
|
+
model_path(@model.id, 'invoke-with-response-stream')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Bedrock counts tokens for the foundation model, not for the
|
|
27
|
+
# inference profiles that serve it.
|
|
28
|
+
def count_tokens_url
|
|
29
|
+
model_path(Protocols::Converse::Chat.foundation_model_id(@model.id), 'count-tokens')
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def render_payload(messages, **)
|
|
33
|
+
payload = super.except(:model, :stream)
|
|
34
|
+
payload[:messages] = inline_url_sources(payload[:messages])
|
|
35
|
+
payload[:system] = inline_url_sources(payload[:system]) if payload[:system]
|
|
36
|
+
payload.merge(anthropic_version: ANTHROPIC_VERSION)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def parse_count_tokens_response(response)
|
|
40
|
+
response.body['inputTokens']
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# The body names the foundation model; pricing and the transcript
|
|
44
|
+
# follow the inference profile or ARN that was called.
|
|
45
|
+
def parse_response_model_id(data)
|
|
46
|
+
called_model_id(super)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def parse_model_id(data)
|
|
50
|
+
called_model_id(super)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def called_model_id(named)
|
|
54
|
+
named && (@model&.id || named)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def provider_file_references?
|
|
58
|
+
false
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def sync_response(payload, additional_headers = {})
|
|
64
|
+
headers = beta_headers(additional_headers, payload)
|
|
65
|
+
response = @connection.post(completion_url, JSON.generate(render_invoke_body(payload, headers)),
|
|
66
|
+
usage: @usage_tracker) do |request|
|
|
67
|
+
request.headers.merge!(without_beta_header(headers))
|
|
68
|
+
end
|
|
69
|
+
parse_completion_response(response)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def stream_events(url, payload, additional_headers = {}, &)
|
|
73
|
+
body = JSON.generate(render_invoke_body(payload, additional_headers))
|
|
74
|
+
super(url, body, without_beta_header(additional_headers), &)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def post_count_tokens(payload)
|
|
78
|
+
body = payload.except(:model).merge(anthropic_version: ANTHROPIC_VERSION, max_tokens: 1)
|
|
79
|
+
body = JSON.generate(render_invoke_body(body, beta_headers({}, payload)))
|
|
80
|
+
request = { input: { invokeModel: { body: Base64.strict_encode64(body) } } }
|
|
81
|
+
@connection.post(count_tokens_url, JSON.generate(request))
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Bedrock still gates the tool search tool behind its beta.
|
|
85
|
+
def beta_headers(additional_headers, payload)
|
|
86
|
+
headers = super
|
|
87
|
+
tools = Array(payload[:tools] || payload['tools'])
|
|
88
|
+
tools.any? { |tool| search_tool?(tool) } ? add_beta(headers, TOOL_SEARCH_BETA) : headers
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# InvokeModel takes Anthropic's betas in the body, not as a header.
|
|
92
|
+
def render_invoke_body(payload, headers)
|
|
93
|
+
betas = canonical_beta_headers(headers)[BETA_HEADER].to_s.split(',').map(&:strip).reject(&:empty?)
|
|
94
|
+
return payload if betas.empty?
|
|
95
|
+
|
|
96
|
+
payload.merge(anthropic_beta: (Array(payload[:anthropic_beta]) + betas).uniq)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def without_beta_header(headers)
|
|
100
|
+
canonical_beta_headers(headers).except(BETA_HEADER)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Bedrock does not fetch URLs, so remote images and documents travel
|
|
104
|
+
# as their bytes.
|
|
105
|
+
def inline_url_sources(blocks)
|
|
106
|
+
return blocks unless blocks.is_a?(Array)
|
|
107
|
+
|
|
108
|
+
blocks.map do |block|
|
|
109
|
+
next block unless block.is_a?(Hash)
|
|
110
|
+
next inline_url_source(block) if url_source?(block)
|
|
111
|
+
|
|
112
|
+
block[:content].is_a?(Array) ? block.merge(content: inline_url_sources(block[:content])) : block
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def url_source?(block)
|
|
117
|
+
source = block[:source]
|
|
118
|
+
source.is_a?(Hash) && source[:type] == 'url'
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def inline_url_source(block)
|
|
122
|
+
attachment = Attachment.new(block.dig(:source, :url))
|
|
123
|
+
block.merge(source: { type: 'base64', media_type: attachment.mime_type, data: attachment.encoded })
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Application inference profile ARNs contain '/', which Bedrock
|
|
127
|
+
# expects escaped inside the model segment.
|
|
128
|
+
def model_path(model_id, action)
|
|
129
|
+
"/model/#{model_id.to_s.gsub('/', '%2F')}/#{action}"
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -23,7 +23,7 @@ module RubyLLM
|
|
|
23
23
|
payload_hash = Digest::SHA256.hexdigest(body)
|
|
24
24
|
|
|
25
25
|
headers = {
|
|
26
|
-
'host' =>
|
|
26
|
+
'host' => signed_host(base_url),
|
|
27
27
|
'x-amz-content-sha256' => payload_hash,
|
|
28
28
|
'x-amz-date' => amz_date
|
|
29
29
|
}
|
|
@@ -85,12 +85,21 @@ module RubyLLM
|
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
conn.url_prefix = base_url
|
|
88
|
+
path = url.delete_prefix('/')
|
|
89
|
+
sent = conn.build_url(path).request_uri
|
|
88
90
|
|
|
89
|
-
conn.public_send(method,
|
|
90
|
-
req.headers.merge!(sign_headers(method.to_s.upcase,
|
|
91
|
+
conn.public_send(method, path, payload) do |req|
|
|
92
|
+
req.headers.merge!(sign_headers(method.to_s.upcase, sent, body, base_url:, service:))
|
|
91
93
|
end
|
|
92
94
|
end
|
|
93
95
|
|
|
96
|
+
# HTTP clients send the port in the Host header unless it is the
|
|
97
|
+
# scheme's default.
|
|
98
|
+
def signed_host(base_url)
|
|
99
|
+
uri = URI.parse(base_url)
|
|
100
|
+
uri.port == uri.default_port ? uri.host : "#{uri.host}:#{uri.port}"
|
|
101
|
+
end
|
|
102
|
+
|
|
94
103
|
def canonical_query_string(raw_query)
|
|
95
104
|
return '' if raw_query.nil? || raw_query.empty?
|
|
96
105
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Providers
|
|
5
|
+
class Bedrock
|
|
6
|
+
# Keeps Bedrock's API paths under the configured endpoint. Faraday
|
|
7
|
+
# resolves a leading slash against the host, which would drop the path of
|
|
8
|
+
# an endpoint mounted behind a gateway.
|
|
9
|
+
class Connection < Transport::Connection # :nodoc:
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def prepare(request)
|
|
13
|
+
super
|
|
14
|
+
request.path = request.path.delete_prefix('/')
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -10,11 +10,21 @@ module RubyLLM
|
|
|
10
10
|
class Responses < Protocols::Responses
|
|
11
11
|
include Mantle
|
|
12
12
|
|
|
13
|
+
# AWS Web Search fetches pages as part of the web_search tool, with
|
|
14
|
+
# no separate fetch tool to enable.
|
|
15
|
+
SERVER_TOOL_ALIASES = Protocols::Responses::SERVER_TOOL_ALIASES.merge(
|
|
16
|
+
web_fetch: { tool: { type: 'web_search' } }
|
|
17
|
+
).freeze
|
|
18
|
+
|
|
19
|
+
def server_tool_aliases
|
|
20
|
+
SERVER_TOOL_ALIASES
|
|
21
|
+
end
|
|
22
|
+
|
|
13
23
|
def completion_url
|
|
14
|
-
|
|
24
|
+
"#{openai_api_path}/responses"
|
|
15
25
|
end
|
|
16
26
|
|
|
17
|
-
def
|
|
27
|
+
def provider_file_references?
|
|
18
28
|
false
|
|
19
29
|
end
|
|
20
30
|
end
|
|
@@ -64,7 +64,7 @@ module RubyLLM
|
|
|
64
64
|
raise UnsupportedAttachmentError, attachment.mime_type unless attachment.audio?
|
|
65
65
|
|
|
66
66
|
content = [
|
|
67
|
-
Protocols::ChatCompletions::Media.
|
|
67
|
+
Protocols::ChatCompletions::Media.render_audio(attachment),
|
|
68
68
|
{ type: 'text', text: transcription_prompt(prompt, language) }
|
|
69
69
|
]
|
|
70
70
|
payload = { model:, messages: [{ role: 'user', content: }], temperature: temperature || 0 }
|
|
@@ -30,6 +30,13 @@ module RubyLLM
|
|
|
30
30
|
|
|
31
31
|
private
|
|
32
32
|
|
|
33
|
+
# OpenAI's GPT models answer only under /openai/v1, and every other
|
|
34
|
+
# model, gpt-oss included, only under /v1.
|
|
35
|
+
def openai_api_path
|
|
36
|
+
id = @model&.id.to_s
|
|
37
|
+
id.start_with?('openai.') && !id.start_with?('openai.gpt-oss') ? 'openai/v1' : 'v1'
|
|
38
|
+
end
|
|
39
|
+
|
|
33
40
|
def sync_response(payload, additional_headers = {})
|
|
34
41
|
parse_completion_response signed_post(completion_url, payload, additional_headers)
|
|
35
42
|
end
|
|
@@ -25,7 +25,7 @@ module RubyLLM
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def models_dev_alias(model_id, models_dev_by_key, _provider_model = nil)
|
|
28
|
-
normalized_id = model_id.sub(
|
|
28
|
+
normalized_id = model_id.sub(/\A(?:#{Protocols::Converse::REGION_PREFIXES.join('|')})\./, '')
|
|
29
29
|
context_override = nil
|
|
30
30
|
normalized_id = normalized_id.gsub(/:(\d+)k\b/) do
|
|
31
31
|
context_override = Regexp.last_match(1).to_i * 1000
|
|
@@ -209,12 +209,6 @@ module RubyLLM
|
|
|
209
209
|
inference_types.include?('INFERENCE_PROFILE') && !inference_types.include?('ON_DEMAND')
|
|
210
210
|
end
|
|
211
211
|
|
|
212
|
-
def normalize_inference_profile_id(model_id, inference_types, region)
|
|
213
|
-
return model_id unless inference_profile_only?(inference_types)
|
|
214
|
-
|
|
215
|
-
with_region_prefix(model_id, region)
|
|
216
|
-
end
|
|
217
|
-
|
|
218
212
|
def with_region_prefix(model_id, region)
|
|
219
213
|
prefixed_with(model_id, region_prefix(region))
|
|
220
214
|
end
|
|
@@ -8,6 +8,7 @@ module RubyLLM
|
|
|
8
8
|
include Bedrock::Models
|
|
9
9
|
|
|
10
10
|
protocol :converse, Protocols::Converse, batches: Protocols::Converse::Batches
|
|
11
|
+
protocol :anthropic, Bedrock::Anthropic, batches: Bedrock::Anthropic::Batches
|
|
11
12
|
protocol :mantle_anthropic, Bedrock::Mantle::Anthropic
|
|
12
13
|
protocol :mantle_responses, Bedrock::Mantle::Responses
|
|
13
14
|
protocol :mantle_chat_completions, Bedrock::Mantle::ChatCompletions
|
|
@@ -50,6 +51,14 @@ module RubyLLM
|
|
|
50
51
|
Models.models_dev_alias(...)
|
|
51
52
|
end
|
|
52
53
|
|
|
54
|
+
def self.open_telemetry_name # :nodoc:
|
|
55
|
+
'aws.bedrock'
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.models_dev_provider_ids # :nodoc:
|
|
59
|
+
%w[amazon-bedrock]
|
|
60
|
+
end
|
|
61
|
+
|
|
53
62
|
def initialize(config) # :nodoc:
|
|
54
63
|
super
|
|
55
64
|
@connection = signed_connection(api_base)
|
|
@@ -64,9 +73,14 @@ module RubyLLM
|
|
|
64
73
|
return protocols[:rerank] if operation == :rerank
|
|
65
74
|
return video_protocol_for(model_id) if operation == :animate
|
|
66
75
|
return protocols[:voxtral_transcription] if voxtral_transcription?(operation, model_id)
|
|
67
|
-
return mantle_protocol_for(model_id) if mantle_model?(model_id)
|
|
68
76
|
|
|
69
|
-
super
|
|
77
|
+
chat_protocol_for(model_id) || super
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Claude speaks the Anthropic Messages API through InvokeModel, which
|
|
81
|
+
# keeps features Converse drops, such as tool search.
|
|
82
|
+
def claude_model?(model_id) # :nodoc:
|
|
83
|
+
Protocols::Converse::Chat.foundation_model_id(model_id).start_with?('anthropic.claude')
|
|
70
84
|
end
|
|
71
85
|
|
|
72
86
|
# Un-versioned ids such as anthropic.claude-sonnet-5 and
|
|
@@ -77,7 +91,8 @@ module RubyLLM
|
|
|
77
91
|
end
|
|
78
92
|
|
|
79
93
|
def mantle_api_base
|
|
80
|
-
|
|
94
|
+
configured_api_base(:bedrock_mantle_api_base, "https://bedrock-mantle.#{bedrock_region}.api.aws",
|
|
95
|
+
endpoints: %r{/(?:chat/completions|responses|messages)\z})
|
|
81
96
|
end
|
|
82
97
|
|
|
83
98
|
def mantle_connection
|
|
@@ -85,7 +100,8 @@ module RubyLLM
|
|
|
85
100
|
end
|
|
86
101
|
|
|
87
102
|
def api_base
|
|
88
|
-
|
|
103
|
+
configured_api_base(:bedrock_api_base, "https://bedrock-runtime.#{bedrock_region}.amazonaws.com",
|
|
104
|
+
endpoints: %r{/model/.*\z})
|
|
89
105
|
end
|
|
90
106
|
|
|
91
107
|
# Uploads are objects in the configured bucket, readable with any
|
|
@@ -96,11 +112,13 @@ module RubyLLM
|
|
|
96
112
|
end
|
|
97
113
|
|
|
98
114
|
def control_api_base
|
|
99
|
-
|
|
115
|
+
configured_api_base(:bedrock_api_base, "https://bedrock.#{bedrock_region}.amazonaws.com",
|
|
116
|
+
endpoints: %r{/model/.*\z})
|
|
100
117
|
end
|
|
101
118
|
|
|
102
119
|
def agent_api_base # :nodoc:
|
|
103
|
-
|
|
120
|
+
configured_api_base(:bedrock_api_base, "https://bedrock-agent-runtime.#{bedrock_region}.amazonaws.com",
|
|
121
|
+
endpoints: %r{/model/.*\z})
|
|
104
122
|
end
|
|
105
123
|
|
|
106
124
|
def agent_connection # :nodoc:
|
|
@@ -131,6 +149,10 @@ module RubyLLM
|
|
|
131
149
|
|
|
132
150
|
def batch_cost_multiplier(**) = 0.5
|
|
133
151
|
|
|
152
|
+
def invoke_model_batch_protocol(model_id) # :nodoc:
|
|
153
|
+
claude_model?(model_id) ? protocols[:anthropic] : embedding_batch_protocol(model_id)
|
|
154
|
+
end
|
|
155
|
+
|
|
134
156
|
def embedding_batch_protocol(model_id) # :nodoc:
|
|
135
157
|
case model_id
|
|
136
158
|
when 'amazon.titan-embed-text-v2:0' then protocols[:titan_text_embeddings]
|
|
@@ -146,6 +168,13 @@ module RubyLLM
|
|
|
146
168
|
body['message'] || body['Message'] || nested_error_message(body) || body['__type'] || super
|
|
147
169
|
end
|
|
148
170
|
|
|
171
|
+
def parse_error_code(response)
|
|
172
|
+
type = response.headers['x-amzn-errortype'] if response.respond_to?(:headers) && response.headers
|
|
173
|
+
type ||= parse_error_body(response).then { |body| body['__type'] if body.is_a?(Hash) }
|
|
174
|
+
code = type.to_s.split(':').first.to_s.split('#').last
|
|
175
|
+
code&.match?(Error::ERROR_CODE) ? code : super
|
|
176
|
+
end
|
|
177
|
+
|
|
149
178
|
# bedrock-mantle nests code, message, and type under "error" rather
|
|
150
179
|
# than answering with a bare string.
|
|
151
180
|
def nested_error_message(body)
|
|
@@ -219,7 +248,7 @@ module RubyLLM
|
|
|
219
248
|
signed = sign_headers(env.method.to_s.upcase, env.url.request_uri, env.body.to_s, base_url:, service:)
|
|
220
249
|
{ 'X-Amz-Security-Token' => nil }.merge(signed)
|
|
221
250
|
end
|
|
222
|
-
|
|
251
|
+
Connection.new(self, @config, api_base: base_url, signer:)
|
|
223
252
|
end
|
|
224
253
|
|
|
225
254
|
def voxtral_transcription?(operation, model_id)
|
|
@@ -227,8 +256,12 @@ module RubyLLM
|
|
|
227
256
|
end
|
|
228
257
|
|
|
229
258
|
def batch_protocol_for(requests)
|
|
230
|
-
return
|
|
259
|
+
return embedding_batch_protocol_for(requests) if requests.any? { |request| request.key?(:text) }
|
|
231
260
|
|
|
261
|
+
requests.all? { |request| anthropic_payload?(request[:payload]) } ? protocols[:anthropic] : super
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def embedding_batch_protocol_for(requests)
|
|
232
265
|
kinds = requests.map { |request| embedding_batch_protocol(request.fetch(:model)) }.uniq
|
|
233
266
|
unless requests.all? { |request| request.key?(:text) } && kinds.one? && kinds.first
|
|
234
267
|
raise Error, 'Bedrock embedding batches require one supported Titan embedding model'
|
|
@@ -237,6 +270,10 @@ module RubyLLM
|
|
|
237
270
|
kinds.first
|
|
238
271
|
end
|
|
239
272
|
|
|
273
|
+
def anthropic_payload?(payload)
|
|
274
|
+
payload.is_a?(Hash) && (payload.key?(:anthropic_version) || payload.key?('anthropic_version'))
|
|
275
|
+
end
|
|
276
|
+
|
|
240
277
|
def bedrock_region
|
|
241
278
|
@config.bedrock_region
|
|
242
279
|
end
|
|
@@ -258,6 +295,12 @@ module RubyLLM
|
|
|
258
295
|
parse_mantle_models_response(response, slug)
|
|
259
296
|
end
|
|
260
297
|
|
|
298
|
+
def chat_protocol_for(model_id)
|
|
299
|
+
return mantle_protocol_for(model_id) if mantle_model?(model_id)
|
|
300
|
+
|
|
301
|
+
protocols[:anthropic] if claude_model?(model_id)
|
|
302
|
+
end
|
|
303
|
+
|
|
261
304
|
def mantle_protocol_for(model_id)
|
|
262
305
|
return protocols[:mantle_anthropic] if model_id.start_with?('anthropic.')
|
|
263
306
|
return protocols[:mantle_responses] if Bedrock::Mantle::RESPONSES_MODELS.include?(model_id)
|
|
@@ -8,7 +8,7 @@ module RubyLLM
|
|
|
8
8
|
protocol :files, Protocols::Cohere::Datasets
|
|
9
9
|
|
|
10
10
|
def api_base
|
|
11
|
-
|
|
11
|
+
configured_api_base(:cohere_api_base, 'https://api.cohere.com', endpoints: %r{/v2/(?:chat|embed|rerank)\z})
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def headers
|
|
@@ -18,6 +18,10 @@ module RubyLLM
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
class << self
|
|
21
|
+
def models_dev_provider_ids # :nodoc:
|
|
22
|
+
%w[cohere]
|
|
23
|
+
end
|
|
24
|
+
|
|
21
25
|
def configuration_options
|
|
22
26
|
%i[cohere_api_key cohere_api_base]
|
|
23
27
|
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Providers
|
|
5
|
+
class Deepgram
|
|
6
|
+
# Deepgram's dialect of Chat Completions on POST /v1/think. It takes
|
|
7
|
+
# the full history on each request and rejects any field it does not
|
|
8
|
+
# list, including the prompt cache fields.
|
|
9
|
+
module Chat
|
|
10
|
+
def completion_url
|
|
11
|
+
@provider.think_url
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Think-Fast rejects the developer role.
|
|
15
|
+
def render_role(role)
|
|
16
|
+
role.to_s
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def openai_prompt_caching?
|
|
20
|
+
false
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -2,12 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
module RubyLLM
|
|
4
4
|
module Providers
|
|
5
|
-
# Integrates Deepgram transcription
|
|
5
|
+
# Integrates Deepgram transcription, speech synthesis, and chat.
|
|
6
6
|
class Deepgram < Provider
|
|
7
|
+
# Deepgram's dialect of the Chat Completions API.
|
|
8
|
+
class ChatCompletions < Protocols::ChatCompletions
|
|
9
|
+
include Deepgram::Chat
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
THINK_BASE = 'https://agent.deepgram.com'
|
|
13
|
+
private_constant :THINK_BASE
|
|
14
|
+
|
|
7
15
|
protocol :deepgram, Protocols::Deepgram
|
|
16
|
+
protocol :chat_completions, ChatCompletions
|
|
8
17
|
|
|
9
18
|
def api_base
|
|
10
|
-
|
|
19
|
+
configured_api_base(:deepgram_api_base, 'https://api.deepgram.com', endpoints: %r{/v1/(?:listen|speak|think)\z})
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Chat runs on its own host. Speech, transcription, and the model
|
|
23
|
+
# listing stay on the Deepgram protocol.
|
|
24
|
+
def protocol_for(model, operation: nil, **)
|
|
25
|
+
operation ? super : protocols[:chat_completions]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def think_url # :nodoc:
|
|
29
|
+
"#{(@config.deepgram_api_base || THINK_BASE).chomp('/')}/v1/think"
|
|
11
30
|
end
|
|
12
31
|
|
|
13
32
|
def headers
|
|
@@ -7,15 +7,16 @@ module RubyLLM
|
|
|
7
7
|
module Chat
|
|
8
8
|
module_function
|
|
9
9
|
|
|
10
|
-
def
|
|
10
|
+
def render_role(role)
|
|
11
11
|
role.to_s
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def render_payload(messages, tools:, temperature:, model:, stream: false, max_output_tokens: nil,
|
|
15
15
|
schema: nil, thinking: nil, citations: false, caching: nil, tool_prefs: nil)
|
|
16
|
+
raise ArgumentError, 'DeepSeek Chat Completions cannot enforce a schema. Use protocol: :responses.' if schema
|
|
17
|
+
|
|
16
18
|
payload = super
|
|
17
19
|
configure_thinking_payload(payload, thinking)
|
|
18
|
-
degrade_schema_payload(payload) if schema
|
|
19
20
|
payload
|
|
20
21
|
end
|
|
21
22
|
|
|
@@ -26,9 +27,7 @@ module RubyLLM
|
|
|
26
27
|
def configure_thinking_payload(payload, thinking)
|
|
27
28
|
return unless thinking&.enabled?
|
|
28
29
|
|
|
29
|
-
if thinking.budget
|
|
30
|
-
RubyLLM.logger.debug { "DeepSeek has no thinking budgets; ignoring budget #{thinking.budget}" }
|
|
31
|
-
end
|
|
30
|
+
raise ArgumentError, 'DeepSeek has no thinking budget; use effort:' if thinking.budget
|
|
32
31
|
|
|
33
32
|
if thinking.disabled?
|
|
34
33
|
payload.delete(:reasoning_effort)
|
|
@@ -38,15 +37,7 @@ module RubyLLM
|
|
|
38
37
|
end
|
|
39
38
|
end
|
|
40
39
|
|
|
41
|
-
def
|
|
42
|
-
RubyLLM.logger.warn(
|
|
43
|
-
'DeepSeek Chat Completions does not support json_schema response formats. ' \
|
|
44
|
-
'Use protocol: :responses to enforce the schema. Falling back to json_object mode.'
|
|
45
|
-
)
|
|
46
|
-
payload[:response_format] = { type: 'json_object' }
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def format_thinking(msg)
|
|
40
|
+
def render_thinking(msg)
|
|
50
41
|
return {} unless msg.role == :assistant
|
|
51
42
|
|
|
52
43
|
thinking = msg.thinking
|
|
@@ -63,8 +54,8 @@ module RubyLLM
|
|
|
63
54
|
)
|
|
64
55
|
end
|
|
65
56
|
|
|
66
|
-
def
|
|
67
|
-
Protocols::ChatCompletions::Media.
|
|
57
|
+
def render_content(content, attachments = [])
|
|
58
|
+
Protocols::ChatCompletions::Media.render_content(
|
|
68
59
|
content,
|
|
69
60
|
attachments,
|
|
70
61
|
document_attachments: :none,
|
|
@@ -8,6 +8,7 @@ module RubyLLM
|
|
|
8
8
|
# instead of summaries, both in responses and in the stream.
|
|
9
9
|
class Responses < Protocols::Responses
|
|
10
10
|
SERVER_TOOL_ALIASES = {
|
|
11
|
+
web_search: { tool: { type: 'web_search' } },
|
|
11
12
|
apply_patch: { tool: { type: 'custom', name: 'apply_patch' } }
|
|
12
13
|
}.freeze
|
|
13
14
|
|
|
@@ -15,6 +16,22 @@ module RubyLLM
|
|
|
15
16
|
SERVER_TOOL_ALIASES
|
|
16
17
|
end
|
|
17
18
|
|
|
19
|
+
def render_payload(messages, thinking: nil, **)
|
|
20
|
+
raise ArgumentError, 'DeepSeek has no thinking budget; use effort:' if thinking&.budget
|
|
21
|
+
|
|
22
|
+
super
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
SERVER_TOOL_USAGE_NAMES = { 'tool_search' => 'web_search', 'tool_fetch' => 'web_fetch' }.freeze
|
|
26
|
+
|
|
27
|
+
# DeepSeek counts searches and page opens in usage, unlike tool_usage
|
|
28
|
+
# on the other Responses APIs.
|
|
29
|
+
def parse_server_tool_use(response)
|
|
30
|
+
response.dig('usage', 'server_tool_use').to_h.transform_keys do |tool|
|
|
31
|
+
"#{SERVER_TOOL_USAGE_NAMES.fetch(tool, tool)}_requests"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
18
35
|
def apply_end_user(payload, identifier)
|
|
19
36
|
payload.merge(user_id: identifier)
|
|
20
37
|
end
|
|
@@ -33,32 +50,32 @@ module RubyLLM
|
|
|
33
50
|
texts.empty? ? super : texts.join("\n")
|
|
34
51
|
end
|
|
35
52
|
|
|
36
|
-
def
|
|
53
|
+
def render_assistant_items(message)
|
|
37
54
|
items = super
|
|
38
55
|
return items if message.raw_content || message.thinking&.text.to_s.empty? || message.thinking.signature
|
|
39
56
|
|
|
40
|
-
items.unshift(
|
|
57
|
+
items.unshift(render_reasoning_item(message.thinking))
|
|
41
58
|
end
|
|
42
59
|
|
|
43
|
-
def
|
|
60
|
+
def render_reasoning_item(thinking)
|
|
44
61
|
{ type: 'reasoning', content: [{ type: 'reasoning_text', text: thinking.text }] }
|
|
45
62
|
end
|
|
46
63
|
|
|
47
|
-
def
|
|
64
|
+
def render_tool_items(message)
|
|
48
65
|
[{
|
|
49
66
|
type: 'function_call_output',
|
|
50
|
-
call_id: message.
|
|
51
|
-
output:
|
|
67
|
+
call_id: message.provider_call_id,
|
|
68
|
+
output: render_content(message.content, message.attachments)
|
|
52
69
|
}]
|
|
53
70
|
end
|
|
54
71
|
|
|
55
|
-
def
|
|
72
|
+
def render_provider_file(file)
|
|
56
73
|
raise UnsupportedAttachmentError, file.mime_type unless file.image?
|
|
57
74
|
|
|
58
75
|
{ type: 'input_image', file_id: file.provider_file_id }
|
|
59
76
|
end
|
|
60
77
|
|
|
61
|
-
def
|
|
78
|
+
def render_document(document)
|
|
62
79
|
raise UnsupportedAttachmentError, document.mime_type
|
|
63
80
|
end
|
|
64
81
|
|
|
@@ -14,7 +14,7 @@ module RubyLLM
|
|
|
14
14
|
protocol :files, Protocols::DeepSeek::Files
|
|
15
15
|
|
|
16
16
|
def api_base
|
|
17
|
-
|
|
17
|
+
configured_api_base(:deepseek_api_base, 'https://api.deepseek.com', endpoints: Protocols::ChatCompletions::ENDPOINT_PATHS)
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def headers
|
|
@@ -28,6 +28,10 @@ module RubyLLM
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
class << self
|
|
31
|
+
def models_dev_provider_ids # :nodoc:
|
|
32
|
+
%w[deepseek]
|
|
33
|
+
end
|
|
34
|
+
|
|
31
35
|
def capabilities
|
|
32
36
|
DeepSeek::Capabilities
|
|
33
37
|
end
|
|
@@ -14,7 +14,8 @@ module RubyLLM
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def api_base
|
|
17
|
-
|
|
17
|
+
configured_api_base(:elevenlabs_api_base, 'https://api.elevenlabs.io',
|
|
18
|
+
endpoints: %r{/v1/(?:speech-to-text\z|text-to-speech/.*\z)})
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
def headers
|