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
|
@@ -25,7 +25,7 @@ module RubyLLM
|
|
|
25
25
|
# never set.
|
|
26
26
|
class Configuration
|
|
27
27
|
class << self
|
|
28
|
-
def option(key, default = nil) # :nodoc:
|
|
28
|
+
def option(key, default = nil, &normalize) # :nodoc:
|
|
29
29
|
key = key.to_sym
|
|
30
30
|
return if options.include?(key)
|
|
31
31
|
|
|
@@ -33,6 +33,7 @@ module RubyLLM
|
|
|
33
33
|
|
|
34
34
|
define_method("#{key}=") do |value|
|
|
35
35
|
value = nil if value.is_a?(String) && value.strip.empty?
|
|
36
|
+
value = normalize.call(value) if normalize
|
|
36
37
|
instance_variable_set(:"@#{key}", value)
|
|
37
38
|
end
|
|
38
39
|
|
|
@@ -65,8 +66,8 @@ module RubyLLM
|
|
|
65
66
|
# :attr_accessor: default_model
|
|
66
67
|
#
|
|
67
68
|
# The model id used by RubyLLM.chat when no model is given.
|
|
68
|
-
# Default: <tt>'gpt-
|
|
69
|
-
option :default_model, 'gpt-
|
|
69
|
+
# Default: <tt>'gpt-6.1-sol'</tt>.
|
|
70
|
+
option :default_model, 'gpt-6.1-sol'
|
|
70
71
|
|
|
71
72
|
##
|
|
72
73
|
# :attr_accessor: default_embedding_model
|
|
@@ -159,6 +160,15 @@ module RubyLLM
|
|
|
159
160
|
# Optional persistence adapter installed by the Rails integration.
|
|
160
161
|
option :batch_store, nil # :nodoc:
|
|
161
162
|
|
|
163
|
+
##
|
|
164
|
+
# :attr_accessor: broadcast_messages
|
|
165
|
+
#
|
|
166
|
+
# Whether +acts_as_message+ records broadcast a chat's messages and
|
|
167
|
+
# streamed chunks with Turbo. Needs turbo-rails and Action Cable. An
|
|
168
|
+
# +acts_as_message+ model's +broadcasts:+ option overrides it. The
|
|
169
|
+
# chat UI generator turns it on. Default: +false+.
|
|
170
|
+
option :broadcast_messages, false
|
|
171
|
+
|
|
162
172
|
##
|
|
163
173
|
# :attr_accessor: mcp_credential_store
|
|
164
174
|
#
|
|
@@ -186,6 +196,59 @@ module RubyLLM
|
|
|
186
196
|
# registering RubyLLM. Default: +nil+.
|
|
187
197
|
option :mcp_client_id, nil
|
|
188
198
|
|
|
199
|
+
##
|
|
200
|
+
# :attr_accessor: sandbox
|
|
201
|
+
#
|
|
202
|
+
# Where code from a model runs by default, such as the code
|
|
203
|
+
# Chat#with_code_execution executes: an MXC::Sandbox class from the mxc
|
|
204
|
+
# gem, its name as a String, or any sandbox Sandbox describes. Default:
|
|
205
|
+
# +nil+ (nothing runs locally).
|
|
206
|
+
option :sandbox, nil
|
|
207
|
+
|
|
208
|
+
##
|
|
209
|
+
# :attr_accessor: sandbox_backend
|
|
210
|
+
#
|
|
211
|
+
# What isolates an MXC::Sandbox on Linux: +:bubblewrap+, which gives
|
|
212
|
+
# each run its own namespaces, or +:landlock+, which only restricts
|
|
213
|
+
# the files it can open and runs without network access. Other
|
|
214
|
+
# platforms ignore it, and so does a sandbox class that names its own
|
|
215
|
+
# backend. Default: +:bubblewrap+.
|
|
216
|
+
option :sandbox_backend, :bubblewrap
|
|
217
|
+
|
|
218
|
+
##
|
|
219
|
+
# :attr_accessor: package_cache_dir
|
|
220
|
+
#
|
|
221
|
+
# Directory holding the gems and Python packages declared with
|
|
222
|
+
# Chat#with_code_execution, installed once and reused by every run.
|
|
223
|
+
# Defaults to a +packages+ directory in the operating system's user
|
|
224
|
+
# cache directory.
|
|
225
|
+
option :package_cache_dir, -> { (directory = Support::Utils.cache_directory) && File.join(directory, 'packages') }
|
|
226
|
+
|
|
227
|
+
##
|
|
228
|
+
# :attr_accessor: code_execution_max_file_size
|
|
229
|
+
#
|
|
230
|
+
# Bytes a file that Chat#with_code_execution's code writes may have and
|
|
231
|
+
# still come back with the result. Larger files stay out, and the
|
|
232
|
+
# model is told which ones. Default: 10 MB.
|
|
233
|
+
#
|
|
234
|
+
# config.code_execution_max_file_size = 100 * 1024 * 1024
|
|
235
|
+
#
|
|
236
|
+
option :code_execution_max_file_size, 10 * 1024 * 1024
|
|
237
|
+
|
|
238
|
+
##
|
|
239
|
+
# :attr_accessor: file_converters
|
|
240
|
+
#
|
|
241
|
+
# Converters that turn a chat's files into text for the model, as a
|
|
242
|
+
# Hash of MIME types to converters, tried before the ones RubyLLM
|
|
243
|
+
# ships. A type may end in <tt>/*</tt>, like <tt>"image/*"</tt>, and a
|
|
244
|
+
# converter is anything that answers +call+ with an Attachment and
|
|
245
|
+
# returns its text, or +nil+ to pass it on. See FileStore::Converters.
|
|
246
|
+
# Default: <tt>{}</tt>.
|
|
247
|
+
#
|
|
248
|
+
# config.file_converters["image/*"] = RubyLLM::FileStore::Converters::OCR.new
|
|
249
|
+
#
|
|
250
|
+
option :file_converters, -> { {} }
|
|
251
|
+
|
|
189
252
|
# Keeps custom 1.x initializers bootable long enough to run the 2.0
|
|
190
253
|
# upgrade generator. The registry is always RubyLLM-owned in 2.0.
|
|
191
254
|
def model_registry_class=(_value) # :nodoc:
|
|
@@ -206,6 +269,27 @@ module RubyLLM
|
|
|
206
269
|
# Seconds to wait for a response before timing out. Default: 300.
|
|
207
270
|
option :request_timeout, 300
|
|
208
271
|
|
|
272
|
+
##
|
|
273
|
+
# :attr_accessor: connect_timeout
|
|
274
|
+
#
|
|
275
|
+
# Seconds to wait for a connection to open before timing out, or +nil+
|
|
276
|
+
# to wait as long as request_timeout. Default: +nil+.
|
|
277
|
+
option :connect_timeout, nil
|
|
278
|
+
|
|
279
|
+
##
|
|
280
|
+
# :attr_accessor: stream_idle_timeout
|
|
281
|
+
#
|
|
282
|
+
# Seconds a streaming request may go without receiving a byte, from
|
|
283
|
+
# sending the request or since the last chunk, before it fails with a
|
|
284
|
+
# ConnectionError whose +timeout?+ is +true+, as a request_timeout
|
|
285
|
+
# does, so fallbacks take over. +request_timeout+ still bounds sending
|
|
286
|
+
# the request, and opening the connection unless connect_timeout is
|
|
287
|
+
# set. Requests that don't stream ignore it. Default: +nil+, which leaves streams to +request_timeout+.
|
|
288
|
+
#
|
|
289
|
+
# config.request_timeout = 1800
|
|
290
|
+
# config.stream_idle_timeout = 60
|
|
291
|
+
option :stream_idle_timeout
|
|
292
|
+
|
|
209
293
|
##
|
|
210
294
|
# :attr_accessor: max_retries
|
|
211
295
|
#
|
|
@@ -248,6 +332,17 @@ module RubyLLM
|
|
|
248
332
|
# SOCKS5 proxies are supported. Default: +nil+.
|
|
249
333
|
option :http_proxy, nil
|
|
250
334
|
|
|
335
|
+
##
|
|
336
|
+
# :attr_accessor: ssl
|
|
337
|
+
#
|
|
338
|
+
# TLS options for all requests, as a Hash of Faraday's SSL option
|
|
339
|
+
# keys, such as <tt>{ ca_file: '/certs/internal-ca.pem' }</tt> or
|
|
340
|
+
# <tt>{ verify: false }</tt>. A context copies its strings, but shares
|
|
341
|
+
# objects such as a +cert_store+, so give each context its own and
|
|
342
|
+
# leave it unchanged once used. WebSocket connections raise
|
|
343
|
+
# ArgumentError when it is set. Default: +nil+.
|
|
344
|
+
option :ssl, nil
|
|
345
|
+
|
|
251
346
|
##
|
|
252
347
|
# :attr_accessor: tool_concurrency
|
|
253
348
|
#
|
|
@@ -256,6 +351,28 @@ module RubyLLM
|
|
|
256
351
|
# for fibers via the async gem. Default: +false+.
|
|
257
352
|
option :tool_concurrency, false
|
|
258
353
|
|
|
354
|
+
##
|
|
355
|
+
# :attr_accessor: context_budget
|
|
356
|
+
#
|
|
357
|
+
# The shares of the context window every chat and agent keeps tool
|
|
358
|
+
# results within, as Chat#with_context_budget takes them. A chat's or
|
|
359
|
+
# agent's own budget wins, and +nil+ there turns it off. Default:
|
|
360
|
+
# +nil+ (no budget).
|
|
361
|
+
#
|
|
362
|
+
# config.context_budget = { per_result: 0.25, per_request: 0.7 }
|
|
363
|
+
#
|
|
364
|
+
option(:context_budget, nil) { |budget| Chat::ContextBudget.normalize(budget) }
|
|
365
|
+
|
|
366
|
+
##
|
|
367
|
+
# :attr_accessor: max_run_time
|
|
368
|
+
#
|
|
369
|
+
# Seconds a persisted chat's run may go without progress, or a tool
|
|
370
|
+
# call it runs may take, before another process treats it as crashed
|
|
371
|
+
# and resumes it. A run makes progress as it streams and between
|
|
372
|
+
# steps. A process that dies frees its claims within a minute.
|
|
373
|
+
# Default: 600.
|
|
374
|
+
option :max_run_time, 600
|
|
375
|
+
|
|
259
376
|
##
|
|
260
377
|
# :attr_accessor: auto_upload_large_files
|
|
261
378
|
#
|
|
@@ -286,6 +403,27 @@ module RubyLLM
|
|
|
286
403
|
# +:raise+. Default: +:warn+.
|
|
287
404
|
option :deprecation_behavior, :warn
|
|
288
405
|
|
|
406
|
+
##
|
|
407
|
+
# :attr_accessor: redact_error_messages
|
|
408
|
+
#
|
|
409
|
+
# Whether Error#message leaves out what the provider wrote. When
|
|
410
|
+
# +true+, a provider's error reads as its HTTP status, provider, and
|
|
411
|
+
# error code only, such as <tt>openai returned HTTP 400
|
|
412
|
+
# (invalid_request_error)</tt>, because providers often quote the
|
|
413
|
+
# request back. The text stays on Error#provider_message and the raw
|
|
414
|
+
# body on Error#response_body. Default: +false+.
|
|
415
|
+
option :redact_error_messages, false
|
|
416
|
+
|
|
417
|
+
##
|
|
418
|
+
# :attr_accessor: split_think_tags
|
|
419
|
+
#
|
|
420
|
+
# Whether Chat Completions responses that open with a
|
|
421
|
+
# <tt><think>...</think></tt> block move that block into the
|
|
422
|
+
# message's thinking. Turn it on for servers that leave reasoning
|
|
423
|
+
# inline, such as llama.cpp with <tt>--reasoning-format none</tt>.
|
|
424
|
+
# A +reasoning_content+ field still wins. Default: +false+.
|
|
425
|
+
option :split_think_tags, false
|
|
426
|
+
|
|
289
427
|
##
|
|
290
428
|
# :attr_accessor: faraday_adapter
|
|
291
429
|
#
|
|
@@ -332,8 +470,17 @@ module RubyLLM
|
|
|
332
470
|
end
|
|
333
471
|
end
|
|
334
472
|
|
|
473
|
+
# Copies file_converters too, so registering one on a context's copy,
|
|
474
|
+
# <tt>config.file_converters["image/*"] = ...</tt>, leaves the
|
|
475
|
+
# original alone.
|
|
476
|
+
def initialize_copy(source) # :nodoc:
|
|
477
|
+
super
|
|
478
|
+
@file_converters = @file_converters.dup
|
|
479
|
+
@ssl = @ssl.to_h.transform_values { |value| value.is_a?(String) ? value.dup : value } if @ssl
|
|
480
|
+
end
|
|
481
|
+
|
|
335
482
|
def instance_variables # :nodoc:
|
|
336
|
-
super.reject { |ivar| ivar.to_s.match?(/(_id|_key|_secret|_token|_credential_provider)$/) }
|
|
483
|
+
super.reject { |ivar| ivar.to_s.match?(/(_id|_key|_secret|_token|_credential_provider|_proxy|\A@ssl)$/) }
|
|
337
484
|
end
|
|
338
485
|
|
|
339
486
|
# Object#inspect reads the instance variable table directly, so
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
# A ContentFilter is one verdict of the provider's content filtering on a
|
|
5
|
+
# request or its response, such as Azure's hate or protected material
|
|
6
|
+
# filters. They appear on Message#content_filters for providers that
|
|
7
|
+
# report them, whether or not they stopped the response, so you can see
|
|
8
|
+
# why a reply ended with Message#content_filtered? or what came close.
|
|
9
|
+
#
|
|
10
|
+
# response = chat.ask "Summarize this paper"
|
|
11
|
+
# response.content_filtered? # => true
|
|
12
|
+
# response.content_filters.select(&:filtered?).map(&:category) # => ["hate"]
|
|
13
|
+
#
|
|
14
|
+
# A verdict names a category and a severity, never the text it judged.
|
|
15
|
+
class ContentFilter
|
|
16
|
+
include Support::Inspectable
|
|
17
|
+
|
|
18
|
+
SOURCES = %i[prompt completion].freeze # :nodoc:
|
|
19
|
+
|
|
20
|
+
# Which side was judged: +:prompt+ for the request, +:completion+ for
|
|
21
|
+
# the response.
|
|
22
|
+
attr_reader :source
|
|
23
|
+
|
|
24
|
+
# The provider's name for the category, such as <tt>"hate"</tt>,
|
|
25
|
+
# <tt>"jailbreak"</tt>, or <tt>"protected_material_text"</tt>.
|
|
26
|
+
attr_reader :category
|
|
27
|
+
|
|
28
|
+
# The severity the provider gave, such as <tt>"safe"</tt>,
|
|
29
|
+
# <tt>"low"</tt>, or <tt>"medium"</tt>, or +nil+ for a category
|
|
30
|
+
# judged only as detected or not.
|
|
31
|
+
attr_reader :severity
|
|
32
|
+
|
|
33
|
+
def self.from_h(data) # :nodoc:
|
|
34
|
+
data = data.transform_keys(&:to_sym)
|
|
35
|
+
new(source: data[:source], category: data[:category], severity: data[:severity],
|
|
36
|
+
filtered: data[:filtered], detected: data[:detected])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def initialize(source:, category:, severity: nil, filtered: false, detected: nil) # :nodoc:
|
|
40
|
+
@source = source&.to_sym
|
|
41
|
+
@category = category.to_s
|
|
42
|
+
@severity = severity
|
|
43
|
+
@filtered = filtered == true
|
|
44
|
+
@detected = detected
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Returns whether the filter blocked or cut the text it judged.
|
|
48
|
+
def filtered?
|
|
49
|
+
@filtered
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Returns whether the provider detected what the category looks for,
|
|
53
|
+
# such as a jailbreak attempt or protected material, or +nil+ for a
|
|
54
|
+
# category judged by severity.
|
|
55
|
+
def detected?
|
|
56
|
+
@detected
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Returns the verdict as a Hash, omitting +nil+ values.
|
|
60
|
+
def to_h
|
|
61
|
+
{ source: source, category: category, severity: severity, filtered: filtered?, detected: @detected }.compact
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def inspect_attributes # :nodoc:
|
|
65
|
+
to_h
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
data/lib/ruby_llm/cost.rb
CHANGED
|
@@ -13,9 +13,14 @@ module RubyLLM
|
|
|
13
13
|
# cost.output
|
|
14
14
|
#
|
|
15
15
|
# The components are RubyLLM's normalized token buckets: #input, #output,
|
|
16
|
-
# #cache_read, #cache_write, and #thinking
|
|
17
|
-
#
|
|
18
|
-
#
|
|
16
|
+
# #cache_read, #cache_write, and #thinking, plus #characters,
|
|
17
|
+
# #audio_seconds, and #search_units for operations billed in those units.
|
|
18
|
+
# When the registry lacks pricing for usage that was reported, the
|
|
19
|
+
# affected component and #total return +nil+ instead of a false zero.
|
|
20
|
+
#
|
|
21
|
+
# speech = RubyLLM.speak "Hello"
|
|
22
|
+
# speech.cost.characters # => 0.00015, or nil when no price is known
|
|
23
|
+
#
|
|
19
24
|
#
|
|
20
25
|
# When the provider reports the exact cost of a call, #total returns the
|
|
21
26
|
# reported amount instead of a registry-price estimate, even when
|
|
@@ -28,7 +33,8 @@ module RubyLLM
|
|
|
28
33
|
class Cost
|
|
29
34
|
include Support::Inspectable
|
|
30
35
|
|
|
31
|
-
|
|
36
|
+
UNITS = %i[characters audio_seconds search_units].freeze # :nodoc:
|
|
37
|
+
COMPONENTS = (%i[input output cache_read cache_write thinking] + UNITS).freeze # :nodoc:
|
|
32
38
|
PER_MILLION = 1_000_000.0 # :nodoc:
|
|
33
39
|
|
|
34
40
|
class << self
|
|
@@ -58,33 +64,43 @@ module RubyLLM
|
|
|
58
64
|
|
|
59
65
|
# Rebuilds a cost from a stored breakdown Hash, as produced by #to_h
|
|
60
66
|
# and persisted alongside a usage entry. Keys may be Strings or
|
|
61
|
-
# Symbols
|
|
62
|
-
#
|
|
67
|
+
# Symbols, and amounts may be numeric Strings, as JSON encodes
|
|
68
|
+
# decimals. The component readers return the recorded amounts and
|
|
69
|
+
# #total equals the recorded +:total+. A breakdown with a total omits
|
|
70
|
+
# the components that had no usage, so ::aggregate still sums them
|
|
71
|
+
# across costs. Pass +tokens+ to mark used components without a
|
|
72
|
+
# recorded amount as missing pricing.
|
|
63
73
|
#
|
|
64
74
|
# RubyLLM::Cost.from_h({ input: 0.0001, total: 0.0003 }).total
|
|
65
75
|
#
|
|
66
76
|
def from_h(hash, tokens: nil)
|
|
67
|
-
amounts = COMPONENTS.to_h { |component| [component, hash
|
|
77
|
+
amounts = COMPONENTS.to_h { |component| [component, recorded_amount(hash, component)] }
|
|
68
78
|
total_recorded = hash.key?(:total) || hash.key?('total')
|
|
69
|
-
total = hash
|
|
70
|
-
missing = missing_recorded_components(amounts, tokens,
|
|
79
|
+
total = recorded_amount(hash, :total) if total_recorded
|
|
80
|
+
missing = missing_recorded_components(amounts, tokens, total)
|
|
71
81
|
|
|
72
82
|
new(amounts:, missing:, reported: recorded_tokens?(amounts, tokens, total_recorded), total:)
|
|
73
83
|
end
|
|
74
84
|
|
|
75
85
|
private
|
|
76
86
|
|
|
87
|
+
def recorded_amount(hash, key)
|
|
88
|
+
amount = hash[key] || hash[key.to_s]
|
|
89
|
+
amount.is_a?(String) ? Float(amount) : amount
|
|
90
|
+
end
|
|
91
|
+
|
|
77
92
|
def aggregate_total(costs)
|
|
78
93
|
totals = costs.map(&:total)
|
|
79
94
|
totals.sum if totals.any? && totals.none?(&:nil?)
|
|
80
95
|
end
|
|
81
96
|
|
|
82
|
-
def missing_recorded_components(amounts, tokens,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
97
|
+
def missing_recorded_components(amounts, tokens, total)
|
|
98
|
+
if tokens
|
|
99
|
+
COMPONENTS.select { |component| tokens.public_send(component).to_f.positive? && amounts[component].nil? }
|
|
100
|
+
elsif total && amounts.values.any?
|
|
101
|
+
[]
|
|
102
|
+
else
|
|
103
|
+
COMPONENTS.reject { |component| amounts[component] }
|
|
88
104
|
end
|
|
89
105
|
end
|
|
90
106
|
|
|
@@ -145,10 +161,29 @@ module RubyLLM
|
|
|
145
161
|
@amounts[:thinking]
|
|
146
162
|
end
|
|
147
163
|
|
|
164
|
+
# Returns the cost of billed characters in US dollars, or +nil+ when
|
|
165
|
+
# the count or its pricing is unavailable.
|
|
166
|
+
def characters
|
|
167
|
+
@amounts[:characters]
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Returns the cost of billed audio seconds in US dollars, or +nil+ when
|
|
171
|
+
# the duration or its pricing is unavailable.
|
|
172
|
+
def audio_seconds
|
|
173
|
+
@amounts[:audio_seconds]
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Returns the cost of billed search units in US dollars, or +nil+ when
|
|
177
|
+
# the count or its pricing is unavailable.
|
|
178
|
+
def search_units
|
|
179
|
+
@amounts[:search_units]
|
|
180
|
+
end
|
|
181
|
+
|
|
148
182
|
# Returns the sum of all components in US dollars, or the exact amount
|
|
149
183
|
# the provider reported when it reported one. Returns +nil+ when there
|
|
150
184
|
# is no token usage, or when pricing is missing for tokens that were
|
|
151
|
-
# used and the provider reported no cost.
|
|
185
|
+
# used and the provider reported no cost. A calculated total covers only
|
|
186
|
+
# the counts the provider reported, so partial usage can leave out charges.
|
|
152
187
|
def total
|
|
153
188
|
return nil unless @complete
|
|
154
189
|
return nil unless tokens?
|
|
@@ -164,14 +199,7 @@ module RubyLLM
|
|
|
164
199
|
# Returns a hash of component costs in US dollars, plus +:total+,
|
|
165
200
|
# omitting +nil+ values.
|
|
166
201
|
def to_h
|
|
167
|
-
{
|
|
168
|
-
input: input,
|
|
169
|
-
output: output,
|
|
170
|
-
cache_read: cache_read,
|
|
171
|
-
cache_write: cache_write,
|
|
172
|
-
thinking: thinking,
|
|
173
|
-
total: total
|
|
174
|
-
}.compact
|
|
202
|
+
COMPONENTS.to_h { |component| [component, public_send(component)] }.merge(total: total).compact
|
|
175
203
|
end
|
|
176
204
|
|
|
177
205
|
def tokens? # :nodoc:
|
|
@@ -202,9 +230,8 @@ module RubyLLM
|
|
|
202
230
|
def missing_component?(component)
|
|
203
231
|
return image_input_missing? if component == :input && detailed_image_input?
|
|
204
232
|
return cache_write_missing? if component == :cache_write && repriced_cache_writes?
|
|
205
|
-
return false if component == :thinking && !thinking_priced_separately?
|
|
206
233
|
|
|
207
|
-
tokens_for(component).
|
|
234
|
+
tokens_for(component).to_f.positive? && price_for(component).nil?
|
|
208
235
|
end
|
|
209
236
|
|
|
210
237
|
def amount_for(component)
|
|
@@ -213,8 +240,6 @@ module RubyLLM
|
|
|
213
240
|
|
|
214
241
|
token_count = tokens_for(component)
|
|
215
242
|
return nil if token_count.nil?
|
|
216
|
-
|
|
217
|
-
token_count = token_count.to_i
|
|
218
243
|
return 0.0 if token_count.zero?
|
|
219
244
|
|
|
220
245
|
price = price_for(component)
|
|
@@ -237,6 +262,8 @@ module RubyLLM
|
|
|
237
262
|
@tokens.cache_write
|
|
238
263
|
when :thinking
|
|
239
264
|
@tokens.thinking if thinking_priced_separately?
|
|
265
|
+
when *UNITS
|
|
266
|
+
@tokens.public_send(component) if @tokens.respond_to?(component)
|
|
240
267
|
end
|
|
241
268
|
end
|
|
242
269
|
|
|
@@ -247,9 +274,18 @@ module RubyLLM
|
|
|
247
274
|
when :cache_read then text_price(:cache_read_input_per_million, text_pricing.cache_read_input)
|
|
248
275
|
when :cache_write then text_price(:cache_write_input_per_million, text_pricing.cache_write_input)
|
|
249
276
|
when :thinking then text_price(:reasoning_output_per_million, text_pricing.reasoning_output)
|
|
277
|
+
when *UNITS then unit_price(component)
|
|
250
278
|
end
|
|
251
279
|
end
|
|
252
280
|
|
|
281
|
+
def unit_price(unit)
|
|
282
|
+
pricing = @model&.pricing
|
|
283
|
+
return unless pricing
|
|
284
|
+
|
|
285
|
+
category = pricing.public_send(unit)
|
|
286
|
+
@tier == :batch ? category.batch&.input_per_million : category.input
|
|
287
|
+
end
|
|
288
|
+
|
|
253
289
|
def input_price
|
|
254
290
|
return text_input_price if @category == :text_tokens || image_cost?
|
|
255
291
|
|
|
@@ -257,7 +293,6 @@ module RubyLLM
|
|
|
257
293
|
end
|
|
258
294
|
|
|
259
295
|
def output_price
|
|
260
|
-
return image_pricing.output || text_output_price if image_cost?
|
|
261
296
|
return text_output_price if @category == :text_tokens
|
|
262
297
|
|
|
263
298
|
category_pricing.output || text_output_price
|
|
@@ -272,13 +307,9 @@ module RubyLLM
|
|
|
272
307
|
end
|
|
273
308
|
|
|
274
309
|
def text_price(attribute, standard_price)
|
|
275
|
-
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
def applicable_text_tier
|
|
279
|
-
return text_pricing.batch if @tier == :batch
|
|
310
|
+
return text_pricing.batch&.public_send(attribute) if @tier == :batch
|
|
280
311
|
|
|
281
|
-
text_pricing.tier_for(prompt_tokens)
|
|
312
|
+
text_pricing.tier_for(prompt_tokens)&.public_send(attribute) || standard_price
|
|
282
313
|
end
|
|
283
314
|
|
|
284
315
|
def prompt_tokens
|
|
@@ -294,7 +325,6 @@ module RubyLLM
|
|
|
294
325
|
end
|
|
295
326
|
|
|
296
327
|
def category_pricing
|
|
297
|
-
return text_pricing if @category == :text_tokens
|
|
298
328
|
return image_pricing if image_cost?
|
|
299
329
|
|
|
300
330
|
pricing = @model&.pricing
|
|
@@ -365,7 +395,6 @@ module RubyLLM
|
|
|
365
395
|
end
|
|
366
396
|
|
|
367
397
|
def image_input_parts
|
|
368
|
-
text_input_price = applicable_text_tier&.input_per_million || text_pricing.input
|
|
369
398
|
[
|
|
370
399
|
[:text, input_detail('text_tokens'), text_input_price],
|
|
371
400
|
[:image, input_detail('image_tokens'), image_pricing.input || text_input_price]
|
|
@@ -377,12 +406,10 @@ module RubyLLM
|
|
|
377
406
|
end
|
|
378
407
|
|
|
379
408
|
def thinking_priced_separately?
|
|
380
|
-
|
|
381
|
-
reasoning_price = tier&.reasoning_output_per_million || text_pricing.reasoning_output
|
|
409
|
+
reasoning_price = price_for(:thinking)
|
|
382
410
|
return false unless reasoning_price
|
|
383
411
|
|
|
384
|
-
|
|
385
|
-
output_price.nil? || reasoning_price != output_price
|
|
412
|
+
text_output_price.nil? || reasoning_price != text_output_price
|
|
386
413
|
end
|
|
387
414
|
|
|
388
415
|
def normalize_model(model)
|
|
@@ -9,19 +9,20 @@ module RubyLLM
|
|
|
9
9
|
#
|
|
10
10
|
class DownloadedFile < String
|
|
11
11
|
include Support::Inspectable
|
|
12
|
+
include Files::Saving
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
##
|
|
15
|
+
# :method: save
|
|
16
|
+
# :call-seq:
|
|
17
|
+
# save(path)
|
|
18
|
+
#
|
|
18
19
|
# Writes the file bytes to +path+, expanding it first. Returns +path+.
|
|
19
20
|
#
|
|
20
21
|
# file.save("report.pdf")
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
|
|
23
|
+
# Returns the raw file bytes as a String.
|
|
24
|
+
def to_blob
|
|
25
|
+
to_s
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
private
|
data/lib/ruby_llm/embedding.rb
CHANGED
|
@@ -5,7 +5,7 @@ module RubyLLM
|
|
|
5
5
|
# RubyLLM.embed returns one:
|
|
6
6
|
#
|
|
7
7
|
# embedding = RubyLLM.embed("Ruby is a programmer's best friend")
|
|
8
|
-
# embedding.
|
|
8
|
+
# embedding.vector # => [0.018, -0.027, ...]
|
|
9
9
|
#
|
|
10
10
|
# Pass an array to embed several texts in one call on supported models:
|
|
11
11
|
#
|
|
@@ -17,6 +17,7 @@ module RubyLLM
|
|
|
17
17
|
class Embedding
|
|
18
18
|
include Support::Inspectable
|
|
19
19
|
include Accounting::Usage::Result
|
|
20
|
+
include Accounting::Usage::ModelInfo
|
|
20
21
|
|
|
21
22
|
# The embedding vectors. A flat array of floats when a single text
|
|
22
23
|
# was embedded, an array of such arrays when an array of texts was
|
|
@@ -32,6 +33,18 @@ module RubyLLM
|
|
|
32
33
|
# The id of the model that produced the vectors, as a String.
|
|
33
34
|
attr_reader :model
|
|
34
35
|
|
|
36
|
+
# Returns the only vector of this result. Raises Error when it holds
|
|
37
|
+
# several, one for each text of an array, so read #vectors instead.
|
|
38
|
+
#
|
|
39
|
+
# RubyLLM.embed("Ruby").vector # => [0.018, -0.027, ...]
|
|
40
|
+
#
|
|
41
|
+
def vector
|
|
42
|
+
return vectors if !vectors.is_a?(Array) || vectors.all?(Numeric)
|
|
43
|
+
return vectors.first if vectors.size == 1
|
|
44
|
+
|
|
45
|
+
raise Error, "This embedding holds #{vectors.size} vectors; read them with #vectors"
|
|
46
|
+
end
|
|
47
|
+
|
|
35
48
|
def initialize(vectors:, model:, sparse_vectors: nil, input_tokens: nil, reported_cost: nil) # :nodoc:
|
|
36
49
|
@vectors = vectors
|
|
37
50
|
@sparse_vectors = sparse_vectors
|
|
@@ -54,14 +67,6 @@ module RubyLLM
|
|
|
54
67
|
Cost.new(tokens:, model: model_info, category: :embeddings)
|
|
55
68
|
end
|
|
56
69
|
|
|
57
|
-
attr_writer :model_info # :nodoc:
|
|
58
|
-
|
|
59
|
-
def model_info # :nodoc:
|
|
60
|
-
@model_info ||= RubyLLM.models.find(model)
|
|
61
|
-
rescue ModelNotFoundError
|
|
62
|
-
nil
|
|
63
|
-
end
|
|
64
|
-
|
|
65
70
|
# Generates embeddings for +text+ and returns an Embedding. +text+
|
|
66
71
|
# may be a single string or, on supported models, an array of strings.
|
|
67
72
|
# An array produces one vector per string in a single API call.
|
|
@@ -91,7 +96,7 @@ module RubyLLM
|
|
|
91
96
|
# <tt>"RETRIEVAL_DOCUMENT"</tt>, while Bedrock Cohere takes an input
|
|
92
97
|
# type such as <tt>"search_document"</tt>. +title:+ labels the
|
|
93
98
|
# document on Vertex AI and Gemini retrieval tasks. Providers that
|
|
94
|
-
#
|
|
99
|
+
# cannot carry either raise ArgumentError. +with:+ passes one or more media
|
|
95
100
|
# attachments (images, audio, video, PDFs) to embed alongside the
|
|
96
101
|
# text on multimodal embedding models such as Gemini's
|
|
97
102
|
# gemini-embedding-2; providers without multimodal embeddings raise
|
|
@@ -100,7 +105,7 @@ module RubyLLM
|
|
|
100
105
|
# request as-is. +metadata:+ is not sent to the provider; it is
|
|
101
106
|
# attached to the emitted +embedding.ruby_llm+ instrumentation event.
|
|
102
107
|
# +owner:+ attributes the usage to a record, such as a user, and wins
|
|
103
|
-
# over RubyLLM.
|
|
108
|
+
# over RubyLLM.with_owner.
|
|
104
109
|
def self.embed(text,
|
|
105
110
|
model: nil,
|
|
106
111
|
provider: nil,
|
|
@@ -113,40 +118,20 @@ module RubyLLM
|
|
|
113
118
|
provider_options: {},
|
|
114
119
|
metadata: nil,
|
|
115
120
|
owner: nil)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
dimensions:
|
|
130
|
-
task_type: task_type,
|
|
131
|
-
title: title,
|
|
132
|
-
attachment_count: Attachment.wrap(with).size,
|
|
133
|
-
provider_options: provider_options,
|
|
134
|
-
metadata: metadata,
|
|
135
|
-
tokens: empty_tokens,
|
|
136
|
-
cost: Cost.new(tokens: empty_tokens, model:, category: :embeddings)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
RubyLLM.instrument('embedding.ruby_llm', payload, config: config) do |event|
|
|
140
|
-
result = Accounting::Usage.owned_by(owner) do
|
|
141
|
-
provider_instance.embed(text, model:, dimensions:, task_type:, title:, with:, provider_options:)
|
|
142
|
-
end
|
|
143
|
-
event[:result] = result
|
|
144
|
-
event[:response_model] = result.model
|
|
145
|
-
event[:tokens] = result.tokens
|
|
146
|
-
event[:cost] = result.cost
|
|
147
|
-
event[:embedding_dimensions] = vector_dimensions(result.vectors)
|
|
148
|
-
event[:embedding_count] = embedding_count(result.vectors)
|
|
149
|
-
result
|
|
121
|
+
Accounting::Operation.new(
|
|
122
|
+
'embedding', model, default: :default_embedding_model, category: :embeddings, provider:, assume_model_exists:,
|
|
123
|
+
context:, owner:,
|
|
124
|
+
report: lambda { |event, result|
|
|
125
|
+
event[:result] = result
|
|
126
|
+
event[:response_model] = result.model
|
|
127
|
+
event[:tokens] = result.tokens
|
|
128
|
+
event[:cost] = result.cost
|
|
129
|
+
event[:embedding_dimensions] = vector_dimensions(result.vectors)
|
|
130
|
+
event[:embedding_count] = embedding_count(result.vectors)
|
|
131
|
+
}
|
|
132
|
+
).run(input: text, dimensions:, task_type:, title:, attachment_count: Attachment.wrap(with).size,
|
|
133
|
+
provider_options:, metadata:) do |provider_instance, model|
|
|
134
|
+
provider_instance.embed(text, model:, dimensions:, task_type:, title:, with:, provider_options:)
|
|
150
135
|
end
|
|
151
136
|
end
|
|
152
137
|
|