ruby_llm 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +162 -120
- data/app/assets/javascripts/ruby_llm/chat_message_controller.js +163 -0
- data/app/assets/javascripts/ruby_llm/dompurify.js +9 -0
- data/app/assets/javascripts/ruby_llm/marked.js +71 -0
- data/config/importmap.rb +5 -0
- data/lib/generators/ruby_llm/agent/agent_generator.rb +11 -3
- data/lib/generators/ruby_llm/agent/templates/agent.rb.tt +6 -3
- data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +201 -55
- data/lib/generators/ruby_llm/chat_ui/templates/agents/chat_agent.rb.tt +15 -0
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/answers_controller.rb.tt +31 -0
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt +16 -5
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt +27 -9
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/queued_messages_controller.rb.tt +14 -0
- data/lib/generators/ruby_llm/chat_ui/templates/javascript/chat_message_controller.js +1 -0
- data/lib/generators/ruby_llm/chat_ui/templates/jobs/chat_response_job.rb.tt +5 -8
- data/lib/generators/ruby_llm/chat_ui/templates/jobs/resume_interrupted_chats_job.rb.tt +7 -0
- data/lib/generators/ruby_llm/chat_ui/templates/migrations/add_reply_error_migration.rb.tt +5 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_files.html.erb.tt +15 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_form.html.erb.tt +5 -7
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_model_select.html.erb.tt +7 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/show.html.erb.tt +32 -3
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_assistant.html.erb.tt +17 -2
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_attachments.html.erb.tt +14 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_error.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_form.html.erb.tt +28 -18
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_queued_message.html.erb.tt +18 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_system.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_tool_calls.html.erb.tt +4 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_user.html.erb.tt +4 -2
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/tool_calls/_default.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/tool_results/_default.html.erb.tt +4 -2
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/questions/_question.html.erb.tt +101 -0
- data/lib/generators/ruby_llm/chat_ui/templates/test/agents/chat_agent_test.rb.tt +28 -0
- data/lib/generators/ruby_llm/chat_ui/templates/test/application_system_test_case.rb.tt +5 -0
- data/lib/generators/ruby_llm/chat_ui/templates/test/system/chats_test.rb.tt +240 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/answers/create.turbo_stream.erb.tt +6 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_files.html.erb.tt +15 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt +5 -7
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_model_select.html.erb.tt +7 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_questions.html.erb.tt +13 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt +29 -3
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_assistant.html.erb.tt +16 -2
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_attachments.html.erb.tt +14 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_error.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_form.html.erb.tt +26 -16
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_queued_message.html.erb.tt +8 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_system.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool_calls.html.erb.tt +4 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_user.html.erb.tt +3 -2
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt +1 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/tool_calls/_default.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/tool_results/_default.html.erb.tt +3 -2
- data/lib/generators/ruby_llm/chat_ui/templates/views/questions/_question.html.erb.tt +78 -0
- data/lib/generators/ruby_llm/evaluation/evaluation_generator.rb +43 -0
- data/lib/generators/ruby_llm/evaluation/templates/evaluation.rb.tt +9 -0
- data/lib/generators/ruby_llm/evaluation/templates/evaluation.yml.tt +4 -0
- data/lib/generators/ruby_llm/generator_helpers.rb +116 -3
- data/lib/generators/ruby_llm/install/install_generator.rb +5 -47
- data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +4 -0
- data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +5 -3
- data/lib/generators/ruby_llm/install/templates/create_ruby_llm_records_migration.rb.tt +41 -1
- data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +2 -1
- data/lib/generators/ruby_llm/install/templates/message_model.rb.tt +1 -2
- data/lib/generators/ruby_llm/judge/judge_generator.rb +35 -0
- data/lib/generators/ruby_llm/judge/templates/judge.rb.tt +2 -0
- data/lib/generators/ruby_llm/mcp/mcp_generator.rb +58 -0
- data/lib/generators/ruby_llm/mcp/templates/mcp.rb.tt +7 -0
- data/lib/generators/ruby_llm/provider/scaffold.rb +5 -12
- data/lib/generators/ruby_llm/provider/templates/core/provider.rb.erb +6 -0
- data/lib/generators/ruby_llm/provider/templates/gem/chat_spec.rb.erb +0 -1
- data/lib/generators/ruby_llm/provider/templates/gem/chat_streaming_spec.rb.erb +0 -1
- data/lib/generators/ruby_llm/provider/templates/gem/moderation_spec.rb.erb +1 -1
- data/lib/generators/ruby_llm/sandbox/sandbox_generator.rb +68 -0
- data/lib/generators/ruby_llm/sandbox/templates/sandbox.rb.tt +3 -0
- data/lib/generators/ruby_llm/skill/skill_generator.rb +48 -0
- data/lib/generators/ruby_llm/tool/templates/tailwind/tool_call.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/tool/templates/tailwind/tool_result.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/tool/templates/tool.rb.tt +0 -3
- data/lib/generators/ruby_llm/tool/templates/tool_call.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/tool/templates/tool_result.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/tool/tool_generator.rb +1 -5
- data/lib/generators/ruby_llm/upgrade/templates/upgrade_ruby_llm_to_2_2.rb.tt +164 -0
- data/lib/generators/ruby_llm/upgrade/upgrade_generator.rb +41 -3
- data/lib/ruby_llm/accounting/operation.rb +73 -0
- data/lib/ruby_llm/accounting/usage.rb +74 -24
- data/lib/ruby_llm/active_record/acts_as.rb +78 -11
- data/lib/ruby_llm/active_record/attachment_helpers.rb +10 -4
- data/lib/ruby_llm/active_record/batch.rb +67 -9
- data/lib/ruby_llm/active_record/chat_agent_hooks.rb +17 -0
- data/lib/ruby_llm/active_record/chat_approvals.rb +71 -0
- data/lib/ruby_llm/active_record/chat_cancellation.rb +106 -0
- data/lib/ruby_llm/active_record/chat_column_guard.rb +51 -0
- data/lib/ruby_llm/active_record/chat_file.rb +37 -0
- data/lib/ruby_llm/active_record/chat_files.rb +30 -0
- data/lib/ruby_llm/active_record/chat_helper.rb +73 -0
- data/lib/ruby_llm/active_record/chat_methods.rb +522 -277
- data/lib/ruby_llm/active_record/chat_placeholders.rb +67 -0
- data/lib/ruby_llm/active_record/chat_questions.rb +177 -0
- data/lib/ruby_llm/active_record/chat_queue.rb +120 -0
- data/lib/ruby_llm/active_record/chat_reply_error.rb +35 -0
- data/lib/ruby_llm/active_record/chat_run.rb +51 -0
- data/lib/ruby_llm/active_record/chat_tool_call_state.rb +77 -0
- data/lib/ruby_llm/active_record/chunk_methods.rb +49 -0
- data/lib/ruby_llm/active_record/claim_heartbeat.rb +149 -0
- data/lib/ruby_llm/active_record/file_store.rb +269 -0
- data/lib/ruby_llm/active_record/mcp_credential.rb +24 -9
- data/lib/ruby_llm/active_record/message_broadcasting.rb +85 -0
- data/lib/ruby_llm/active_record/message_methods.rb +83 -11
- data/lib/ruby_llm/active_record/model.rb +37 -9
- data/lib/ruby_llm/active_record/provider_file.rb +21 -4
- data/lib/ruby_llm/active_record/queued_message.rb +50 -0
- data/lib/ruby_llm/active_record/record.rb +3 -1
- data/lib/ruby_llm/active_record/run_claims.rb +608 -0
- data/lib/ruby_llm/active_record/solid_queue_shutdown.rb +33 -0
- data/lib/ruby_llm/active_record/system_instructions.rb +30 -0
- data/lib/ruby_llm/active_record/tool_call.rb +55 -4
- data/lib/ruby_llm/active_record/usage.rb +79 -9
- data/lib/ruby_llm/agent/callbacks.rb +98 -0
- data/lib/ruby_llm/agent/context_budget.rb +26 -0
- data/lib/ruby_llm/agent/join_callbacks.rb +28 -0
- data/lib/ruby_llm/agent/model_options.rb +56 -0
- data/lib/ruby_llm/agent/ownership.rb +22 -0
- data/lib/ruby_llm/agent/tool_conversion.rb +19 -0
- data/lib/ruby_llm/agent.rb +620 -236
- data/lib/ruby_llm/agent_tool.rb +91 -0
- data/lib/ruby_llm/aliases.json +84 -91
- data/lib/ruby_llm/attachment.rb +101 -20
- data/lib/ruby_llm/batch.rb +132 -46
- data/lib/ruby_llm/cached_content.rb +4 -4
- data/lib/ruby_llm/chat/agent_run.rb +22 -0
- data/lib/ruby_llm/chat/context_budget.rb +119 -0
- data/lib/ruby_llm/chat/file_references.rb +259 -0
- data/lib/ruby_llm/chat/message_queue.rb +56 -0
- data/lib/ruby_llm/chat/ownership.rb +18 -0
- data/lib/ruby_llm/chat/rescuable.rb +48 -0
- data/lib/ruby_llm/chat/run.rb +32 -0
- data/lib/ruby_llm/chat/tool_call_ids.rb +46 -0
- data/lib/ruby_llm/chat/tool_call_state.rb +115 -0
- data/lib/ruby_llm/chat/tool_concurrency.rb +90 -49
- data/lib/ruby_llm/chat/tool_search.rb +36 -1
- data/lib/ruby_llm/chat/tool_sequences.rb +84 -0
- data/lib/ruby_llm/chat.rb +1598 -332
- data/lib/ruby_llm/chunk.rb +27 -1
- data/lib/ruby_llm/citation.rb +2 -1
- data/lib/ruby_llm/code_execution/language.rb +28 -0
- data/lib/ruby_llm/code_execution/node_installation.rb +56 -0
- data/lib/ruby_llm/code_execution/packages.rb +241 -0
- data/lib/ruby_llm/code_execution/ruby_installation.rb +170 -0
- data/lib/ruby_llm/code_execution/workspace.rb +314 -0
- data/lib/ruby_llm/code_execution.rb +781 -0
- data/lib/ruby_llm/configuration.rb +151 -4
- data/lib/ruby_llm/content_filter.rb +68 -0
- data/lib/ruby_llm/cost.rb +68 -41
- data/lib/ruby_llm/downloaded_file.rb +10 -9
- data/lib/ruby_llm/embedding.rb +30 -45
- data/lib/ruby_llm/embedding_request.rb +1 -1
- data/lib/ruby_llm/error.rb +167 -14
- data/lib/ruby_llm/evaluation/dataset.rb +5 -4
- data/lib/ruby_llm/evaluation/evidence.rb +5 -3
- data/lib/ruby_llm/evaluation/report.rb +7 -3
- data/lib/ruby_llm/evaluation/result.rb +1 -1
- data/lib/ruby_llm/evaluation.rb +2 -1
- data/lib/ruby_llm/fallback.rb +32 -4
- data/lib/ruby_llm/file_store/converters/ocr.rb +28 -0
- data/lib/ruby_llm/file_store/converters/office/markup.rb +148 -0
- data/lib/ruby_llm/file_store/converters/office/outline.rb +193 -0
- data/lib/ruby_llm/file_store/converters/office/prose.rb +161 -0
- data/lib/ruby_llm/file_store/converters/office/workbook.rb +187 -0
- data/lib/ruby_llm/file_store/converters/office/zip.rb +149 -0
- data/lib/ruby_llm/file_store/converters/office.rb +106 -0
- data/lib/ruby_llm/file_store/converters/text.rb +27 -0
- data/lib/ruby_llm/file_store/converters.rb +84 -0
- data/lib/ruby_llm/file_store/directory.rb +190 -0
- data/lib/ruby_llm/file_store/memory.rb +75 -0
- data/lib/ruby_llm/file_store/tools/base.rb +99 -0
- data/lib/ruby_llm/file_store/tools/delete.rb +48 -0
- data/lib/ruby_llm/file_store/tools/edit.rb +155 -0
- data/lib/ruby_llm/file_store/tools/list.rb +57 -0
- data/lib/ruby_llm/file_store/tools/read.rb +120 -0
- data/lib/ruby_llm/file_store/tools/search.rb +208 -0
- data/lib/ruby_llm/file_store/tools/write.rb +40 -0
- data/lib/ruby_llm/file_store/tools.rb +39 -0
- data/lib/ruby_llm/file_store.rb +338 -0
- data/lib/ruby_llm/files/saving.rb +14 -0
- data/lib/ruby_llm/image.rb +67 -45
- data/lib/ruby_llm/input_required_error.rb +28 -0
- data/lib/ruby_llm/judge/builder.rb +6 -1
- data/lib/ruby_llm/judge.rb +1 -1
- data/lib/ruby_llm/judgment.rb +6 -24
- data/lib/ruby_llm/mcp/client.rb +67 -28
- data/lib/ruby_llm/mcp/collection.rb +39 -2
- data/lib/ruby_llm/mcp/content.rb +7 -2
- data/lib/ruby_llm/mcp/error.rb +8 -2
- data/lib/ruby_llm/mcp/http.rb +117 -37
- data/lib/ruby_llm/mcp/input_request.rb +3 -112
- data/lib/ruby_llm/mcp/input_required_error.rb +3 -21
- data/lib/ruby_llm/mcp/insufficient_scope_error.rb +27 -0
- data/lib/ruby_llm/mcp/listener.rb +7 -1
- data/lib/ruby_llm/mcp/oauth.rb +102 -39
- data/lib/ruby_llm/mcp/outages.rb +172 -0
- data/lib/ruby_llm/mcp/resource.rb +9 -6
- data/lib/ruby_llm/mcp/resource_template.rb +3 -2
- data/lib/ruby_llm/mcp/session_expired.rb +10 -2
- data/lib/ruby_llm/mcp/stdio.rb +66 -18
- data/lib/ruby_llm/mcp/task.rb +13 -40
- data/lib/ruby_llm/mcp/tool.rb +34 -11
- data/lib/ruby_llm/mcp.rb +123 -59
- data/lib/ruby_llm/message.rb +217 -23
- data/lib/ruby_llm/model/modalities.rb +1 -1
- data/lib/ruby_llm/model/pricing.rb +26 -4
- data/lib/ruby_llm/model/pricing_category.rb +38 -15
- data/lib/ruby_llm/model/pricing_tier.rb +3 -2
- data/lib/ruby_llm/model.rb +24 -6
- data/lib/ruby_llm/models/lookup.rb +37 -4
- data/lib/ruby_llm/models/registry.rb +4 -19
- data/lib/ruby_llm/models.json +36186 -27832
- data/lib/ruby_llm/models.rb +44 -39
- data/lib/ruby_llm/moderation.rb +10 -30
- data/lib/ruby_llm/ocr.rb +5 -22
- data/lib/ruby_llm/open_telemetry/attributes.rb +68 -19
- data/lib/ruby_llm/open_telemetry/content.rb +105 -0
- data/lib/ruby_llm/open_telemetry.rb +18 -4
- data/lib/ruby_llm/prompt/context.rb +2 -2
- data/lib/ruby_llm/prompt.rb +7 -5
- data/lib/ruby_llm/protocol/binary_streaming.rb +2 -8
- data/lib/ruby_llm/protocol/cache_lifetime.rb +22 -0
- data/lib/ruby_llm/protocol/native_content.rb +69 -0
- data/lib/ruby_llm/protocol/replayed_arguments.rb +28 -0
- data/lib/ruby_llm/protocol/stream_accumulator.rb +73 -33
- data/lib/ruby_llm/protocol/streaming.rb +105 -22
- data/lib/ruby_llm/protocol/tool_namespaces.rb +100 -0
- data/lib/ruby_llm/protocol/unsupported_operations.rb +42 -0
- data/lib/ruby_llm/protocol.rb +134 -109
- data/lib/ruby_llm/protocols/anthropic/batches.rb +41 -4
- data/lib/ruby_llm/protocols/anthropic/block_binding.rb +30 -0
- data/lib/ruby_llm/protocols/anthropic/chat.rb +135 -60
- data/lib/ruby_llm/protocols/anthropic/files.rb +9 -0
- data/lib/ruby_llm/protocols/anthropic/media.rb +14 -14
- data/lib/ruby_llm/protocols/anthropic/models.rb +16 -10
- data/lib/ruby_llm/protocols/anthropic/streaming.rb +43 -22
- data/lib/ruby_llm/protocols/anthropic/tools.rb +36 -24
- data/lib/ruby_llm/protocols/anthropic.rb +61 -6
- data/lib/ruby_llm/protocols/azure/speech/transcription.rb +125 -0
- data/lib/ruby_llm/protocols/azure/speech.rb +14 -0
- data/lib/ruby_llm/protocols/bedrock/batches.rb +26 -8
- data/lib/ruby_llm/protocols/bedrock/event_stream.rb +205 -0
- data/lib/ruby_llm/protocols/bedrock/files.rb +15 -6
- data/lib/ruby_llm/protocols/bedrock/guardrails.rb +1 -1
- data/lib/ruby_llm/protocols/bedrock/rerank.rb +2 -0
- data/lib/ruby_llm/protocols/chat_completions/chat.rb +158 -58
- data/lib/ruby_llm/protocols/chat_completions/content_filters.rb +45 -0
- data/lib/ruby_llm/protocols/chat_completions/embedding_batches.rb +23 -2
- data/lib/ruby_llm/protocols/chat_completions/embeddings.rb +26 -3
- data/lib/ruby_llm/protocols/chat_completions/images.rb +7 -9
- data/lib/ruby_llm/protocols/chat_completions/media.rb +20 -20
- data/lib/ruby_llm/protocols/chat_completions/moderation.rb +8 -5
- data/lib/ruby_llm/protocols/chat_completions/speech.rb +8 -2
- data/lib/ruby_llm/protocols/chat_completions/streaming.rb +38 -17
- data/lib/ruby_llm/protocols/chat_completions/think_tags.rb +148 -0
- data/lib/ruby_llm/protocols/chat_completions/tools.rb +15 -28
- data/lib/ruby_llm/protocols/chat_completions/transcription.rb +17 -2
- data/lib/ruby_llm/protocols/chat_completions.rb +4 -0
- data/lib/ruby_llm/protocols/cohere/batches.rb +15 -8
- data/lib/ruby_llm/protocols/cohere/chat.rb +47 -25
- data/lib/ruby_llm/protocols/cohere/datasets.rb +6 -2
- data/lib/ruby_llm/protocols/cohere/embeddings.rb +7 -5
- data/lib/ruby_llm/protocols/cohere/media.rb +7 -7
- data/lib/ruby_llm/protocols/cohere/rerank.rb +2 -1
- data/lib/ruby_llm/protocols/cohere/streaming.rb +26 -11
- data/lib/ruby_llm/protocols/cohere/tools.rb +17 -28
- data/lib/ruby_llm/protocols/cohere/transcription.rb +5 -4
- data/lib/ruby_llm/protocols/converse/batches.rb +2 -24
- data/lib/ruby_llm/protocols/converse/chat.rb +210 -75
- data/lib/ruby_llm/protocols/converse/media.rb +24 -22
- data/lib/ruby_llm/protocols/converse/reasoning.rb +23 -9
- data/lib/ruby_llm/protocols/converse/streaming.rb +68 -220
- data/lib/ruby_llm/protocols/converse.rb +17 -1
- data/lib/ruby_llm/protocols/deepgram/speech.rb +1 -2
- data/lib/ruby_llm/protocols/deepgram/streaming_transcription.rb +1 -3
- data/lib/ruby_llm/protocols/deepgram/transcription.rb +28 -19
- data/lib/ruby_llm/protocols/deepgram.rb +3 -2
- data/lib/ruby_llm/protocols/deepseek/files.rb +1 -1
- data/lib/ruby_llm/protocols/elevenlabs/assets.rb +6 -4
- data/lib/ruby_llm/protocols/elevenlabs/flows/images.rb +4 -0
- data/lib/ruby_llm/protocols/elevenlabs/speech.rb +3 -3
- data/lib/ruby_llm/protocols/elevenlabs/streaming_transcription.rb +5 -2
- data/lib/ruby_llm/protocols/elevenlabs/transcription.rb +6 -3
- data/lib/ruby_llm/protocols/files.rb +27 -12
- data/lib/ruby_llm/protocols/gemini/caches.rb +4 -4
- data/lib/ruby_llm/protocols/gemini/chat.rb +118 -51
- data/lib/ruby_llm/protocols/gemini/embedding_batches.rb +9 -5
- data/lib/ruby_llm/protocols/gemini/embeddings.rb +3 -3
- data/lib/ruby_llm/protocols/gemini/file_transcription.rb +4 -0
- data/lib/ruby_llm/protocols/gemini/files.rb +5 -4
- data/lib/ruby_llm/protocols/gemini/images.rb +11 -9
- data/lib/ruby_llm/protocols/gemini/live_transcription.rb +1 -1
- data/lib/ruby_llm/protocols/gemini/long_decode.rb +104 -0
- data/lib/ruby_llm/protocols/gemini/media.rb +31 -28
- data/lib/ruby_llm/protocols/gemini/retry_info.rb +29 -0
- data/lib/ruby_llm/protocols/gemini/schemas.rb +126 -0
- data/lib/ruby_llm/protocols/gemini/speech.rb +9 -3
- data/lib/ruby_llm/protocols/gemini/streaming.rb +37 -13
- data/lib/ruby_llm/protocols/gemini/tools.rb +18 -10
- data/lib/ruby_llm/protocols/gemini/transcription.rb +13 -9
- data/lib/ruby_llm/protocols/gemini/videos.rb +14 -3
- data/lib/ruby_llm/protocols/gemini.rb +16 -0
- data/lib/ruby_llm/protocols/gpustack/responses.rb +3 -3
- data/lib/ruby_llm/protocols/gpustack/tokenization.rb +14 -0
- data/lib/ruby_llm/protocols/interactions/chat.rb +29 -14
- data/lib/ruby_llm/protocols/interactions/content.rb +19 -14
- data/lib/ruby_llm/protocols/interactions/streaming.rb +1 -1
- data/lib/ruby_llm/protocols/interactions/tools.rb +10 -13
- data/lib/ruby_llm/protocols/interactions/videos.rb +99 -0
- data/lib/ruby_llm/protocols/interactions.rb +1 -0
- data/lib/ruby_llm/protocols/invoke_model/cohere_embeddings.rb +28 -10
- data/lib/ruby_llm/protocols/invoke_model/embedding_batches.rb +1 -1
- data/lib/ruby_llm/protocols/invoke_model/nova_embeddings.rb +45 -11
- data/lib/ruby_llm/protocols/invoke_model/stability_images.rb +5 -2
- data/lib/ruby_llm/protocols/invoke_model/titan_multimodal_embeddings.rb +11 -3
- data/lib/ruby_llm/protocols/invoke_model.rb +21 -2
- data/lib/ruby_llm/protocols/meta/transcription/stream.rb +108 -0
- data/lib/ruby_llm/protocols/meta/transcription.rb +76 -0
- data/lib/ruby_llm/protocols/minimax/files.rb +58 -0
- data/lib/ruby_llm/protocols/minimax/images.rb +54 -0
- data/lib/ruby_llm/protocols/minimax/speech.rb +53 -0
- data/lib/ruby_llm/protocols/minimax/transcription.rb +82 -0
- data/lib/ruby_llm/protocols/minimax/videos.rb +93 -0
- data/lib/ruby_llm/protocols/minimax.rb +21 -0
- data/lib/ruby_llm/protocols/mistral/conversations/chat.rb +44 -21
- data/lib/ruby_llm/protocols/mistral/files.rb +6 -4
- data/lib/ruby_llm/protocols/mistral/multi_completion.rb +15 -4
- data/lib/ruby_llm/protocols/moonshot/files.rb +48 -0
- data/lib/ruby_llm/protocols/openai/batches.rb +3 -2
- data/lib/ruby_llm/protocols/openai/decisions.rb +6 -4
- data/lib/ruby_llm/protocols/openai/files.rb +13 -6
- data/lib/ruby_llm/protocols/openrouter/batches.rb +13 -17
- data/lib/ruby_llm/protocols/openrouter/responses.rb +6 -10
- data/lib/ruby_llm/protocols/perplexity/agent.rb +19 -35
- data/lib/ruby_llm/protocols/perplexity/batches.rb +37 -0
- data/lib/ruby_llm/protocols/perplexity/files.rb +19 -9
- data/lib/ruby_llm/protocols/responses/approvals.rb +12 -11
- data/lib/ruby_llm/protocols/responses/chat.rb +165 -75
- data/lib/ruby_llm/protocols/responses/citation_markers.rb +167 -0
- data/lib/ruby_llm/protocols/responses/compaction.rb +2 -2
- data/lib/ruby_llm/protocols/responses/media.rb +17 -9
- data/lib/ruby_llm/protocols/responses/streaming.rb +71 -12
- data/lib/ruby_llm/protocols/responses/token_counting.rb +1 -1
- data/lib/ruby_llm/protocols/responses/tools.rb +48 -1
- data/lib/ruby_llm/protocols/responses.rb +21 -0
- data/lib/ruby_llm/protocols/system_one/responses.rb +9 -2
- data/lib/ruby_llm/protocols/vertexai/files.rb +4 -3
- data/lib/ruby_llm/protocols/vertexai/text_to_speech.rb +116 -0
- data/lib/ruby_llm/protocols/xai/files.rb +13 -6
- data/lib/ruby_llm/protocols/zai/files.rb +37 -0
- data/lib/ruby_llm/provider.rb +128 -31
- data/lib/ruby_llm/providers/anthropic.rb +5 -1
- data/lib/ruby_llm/providers/azure/chat.rb +17 -3
- data/lib/ruby_llm/providers/azure/chat_completions.rb +1 -0
- data/lib/ruby_llm/providers/azure/decisions.rb +17 -0
- data/lib/ruby_llm/providers/azure/embeddings.rb +1 -2
- data/lib/ruby_llm/providers/azure/media.rb +6 -6
- data/lib/ruby_llm/providers/azure/mistral.rb +17 -0
- data/lib/ruby_llm/providers/azure/models.rb +16 -2
- data/lib/ruby_llm/providers/azure/moderation.rb +16 -0
- data/lib/ruby_llm/providers/azure/responses.rb +9 -0
- data/lib/ruby_llm/providers/azure/videos.rb +2 -2
- data/lib/ruby_llm/providers/azure.rb +43 -1
- data/lib/ruby_llm/providers/bedrock/anthropic/batches.rb +32 -0
- data/lib/ruby_llm/providers/bedrock/anthropic.rb +134 -0
- data/lib/ruby_llm/providers/bedrock/auth.rb +12 -3
- data/lib/ruby_llm/providers/bedrock/connection.rb +19 -0
- data/lib/ruby_llm/providers/bedrock/mantle/anthropic.rb +5 -1
- data/lib/ruby_llm/providers/bedrock/mantle/chat_completions.rb +2 -2
- data/lib/ruby_llm/providers/bedrock/mantle/responses.rb +12 -2
- data/lib/ruby_llm/providers/bedrock/mantle/voxtral.rb +1 -1
- data/lib/ruby_llm/providers/bedrock/mantle.rb +7 -0
- data/lib/ruby_llm/providers/bedrock/models.rb +2 -9
- data/lib/ruby_llm/providers/bedrock.rb +51 -8
- data/lib/ruby_llm/providers/cohere.rb +5 -1
- data/lib/ruby_llm/providers/deepgram/chat.rb +25 -0
- data/lib/ruby_llm/providers/deepgram.rb +21 -2
- data/lib/ruby_llm/providers/deepseek/chat.rb +7 -16
- data/lib/ruby_llm/providers/deepseek/responses.rb +25 -8
- data/lib/ruby_llm/providers/deepseek.rb +5 -1
- data/lib/ruby_llm/providers/elevenlabs.rb +2 -1
- data/lib/ruby_llm/providers/gemini.rb +13 -1
- data/lib/ruby_llm/providers/gpustack/chat.rb +23 -3
- data/lib/ruby_llm/providers/gpustack/embeddings.rb +2 -2
- data/lib/ruby_llm/providers/gpustack/media.rb +8 -8
- data/lib/ruby_llm/providers/gpustack/transcription.rb +14 -6
- data/lib/ruby_llm/providers/gpustack.rb +1 -1
- data/lib/ruby_llm/providers/hetzner/chat.rb +23 -6
- data/lib/ruby_llm/providers/hetzner.rb +5 -1
- data/lib/ruby_llm/providers/meta/responses.rb +81 -0
- data/lib/ruby_llm/providers/meta.rb +37 -0
- data/lib/ruby_llm/providers/minimax/anthropic.rb +95 -0
- data/lib/ruby_llm/providers/minimax/chat_completions.rb +48 -0
- data/lib/ruby_llm/providers/minimax/responses.rb +52 -0
- data/lib/ruby_llm/providers/minimax.rb +104 -0
- data/lib/ruby_llm/providers/mistral/chat.rb +34 -4
- data/lib/ruby_llm/providers/mistral/chat_completions/batches.rb +8 -3
- data/lib/ruby_llm/providers/mistral/embeddings.rb +1 -12
- data/lib/ruby_llm/providers/mistral/input_tokens.rb +35 -0
- data/lib/ruby_llm/providers/mistral/media.rb +8 -8
- data/lib/ruby_llm/providers/mistral/transcription.rb +8 -6
- data/lib/ruby_llm/providers/mistral.rb +13 -1
- data/lib/ruby_llm/providers/moonshot/capabilities.rb +16 -0
- data/lib/ruby_llm/providers/moonshot/chat.rb +100 -0
- data/lib/ruby_llm/providers/moonshot/documents.rb +79 -0
- data/lib/ruby_llm/providers/moonshot/responses.rb +55 -0
- data/lib/ruby_llm/providers/moonshot.rb +54 -0
- data/lib/ruby_llm/providers/ollama/chat.rb +5 -6
- data/lib/ruby_llm/providers/ollama/embeddings.rb +41 -0
- data/lib/ruby_llm/providers/ollama/media.rb +6 -6
- data/lib/ruby_llm/providers/ollama/speech.rb +24 -0
- data/lib/ruby_llm/providers/ollama.rb +13 -1
- data/lib/ruby_llm/providers/ollama_cloud.rb +22 -1
- data/lib/ruby_llm/providers/openai/responses.rb +8 -0
- data/lib/ruby_llm/providers/openai.rb +7 -3
- data/lib/ruby_llm/providers/openrouter/chat.rb +15 -9
- data/lib/ruby_llm/providers/openrouter/embeddings.rb +12 -8
- data/lib/ruby_llm/providers/openrouter/images.rb +16 -10
- data/lib/ruby_llm/providers/openrouter/judgments.rb +47 -0
- data/lib/ruby_llm/providers/openrouter/media.rb +5 -5
- data/lib/ruby_llm/providers/openrouter/models.rb +19 -7
- data/lib/ruby_llm/providers/openrouter/streaming.rb +4 -4
- data/lib/ruby_llm/providers/openrouter/videos.rb +1 -1
- data/lib/ruby_llm/providers/openrouter.rb +32 -5
- data/lib/ruby_llm/providers/perplexity/chat.rb +3 -3
- data/lib/ruby_llm/providers/perplexity/embeddings.rb +1 -1
- data/lib/ruby_llm/providers/perplexity/media.rb +6 -6
- data/lib/ruby_llm/providers/perplexity.rb +13 -2
- data/lib/ruby_llm/providers/typesafe.rb +1 -1
- data/lib/ruby_llm/providers/vertexai/anthropic.rb +5 -1
- data/lib/ruby_llm/providers/vertexai/credentials.rb +27 -3
- data/lib/ruby_llm/providers/vertexai/embed_content.rb +1 -1
- data/lib/ruby_llm/providers/vertexai/embeddings.rb +2 -3
- data/lib/ruby_llm/providers/vertexai/gemini.rb +1 -1
- data/lib/ruby_llm/providers/vertexai/mistral.rb +5 -0
- data/lib/ruby_llm/providers/vertexai/models.rb +24 -2
- data/lib/ruby_llm/providers/vertexai/transcription.rb +2 -2
- data/lib/ruby_llm/providers/vertexai/videos.rb +1 -1
- data/lib/ruby_llm/providers/vertexai.rb +56 -9
- data/lib/ruby_llm/providers/xai/chat.rb +3 -3
- data/lib/ruby_llm/providers/xai/chat_completions/batches.rb +8 -3
- data/lib/ruby_llm/providers/xai/images.rb +9 -4
- data/lib/ruby_llm/providers/xai/models.rb +2 -2
- data/lib/ruby_llm/providers/xai/responses.rb +10 -0
- data/lib/ruby_llm/providers/xai/speech.rb +2 -2
- data/lib/ruby_llm/providers/xai/transcription.rb +14 -9
- data/lib/ruby_llm/providers/xai/videos.rb +1 -1
- data/lib/ruby_llm/providers/xai.rb +9 -1
- data/lib/ruby_llm/providers/zai/chat.rb +117 -0
- data/lib/ruby_llm/providers/zai/ocr.rb +65 -0
- data/lib/ruby_llm/providers/zai/token_counting.rb +25 -0
- data/lib/ruby_llm/providers/zai/videos.rb +67 -0
- data/lib/ruby_llm/providers/zai.rb +66 -0
- data/lib/ruby_llm/question.rb +390 -0
- data/lib/ruby_llm/queued_message.rb +74 -0
- data/lib/ruby_llm/railtie.rb +43 -0
- data/lib/ruby_llm/request_shape/part.rb +2 -2
- data/lib/ruby_llm/request_shape/problem.rb +2 -2
- data/lib/ruby_llm/request_shape/tool_round.rb +2 -2
- data/lib/ruby_llm/request_shape/turn.rb +2 -2
- data/lib/ruby_llm/request_shape.rb +1 -1
- data/lib/ruby_llm/rerank.rb +10 -37
- data/lib/ruby_llm/research_job.rb +26 -13
- data/lib/ruby_llm/sandbox/landlock.rb +78 -0
- data/lib/ruby_llm/sandbox/mxc.rb +137 -0
- data/lib/ruby_llm/sandbox.rb +141 -0
- data/lib/ruby_llm/search_results.rb +9 -4
- data/lib/ruby_llm/server_tool_call.rb +1 -1
- data/lib/ruby_llm/skill/loader.rb +131 -0
- data/lib/ruby_llm/skill.rb +263 -0
- data/lib/ruby_llm/speech.rb +25 -53
- data/lib/ruby_llm/stored_file.rb +94 -0
- data/lib/ruby_llm/support/cancellation.rb +4 -4
- data/lib/ruby_llm/support/deprecated_enumerable.rb +13 -0
- data/lib/ruby_llm/support/deprecated_job_predicates.rb +24 -0
- data/lib/ruby_llm/support/inspectable.rb +1 -1
- data/lib/ruby_llm/support/instrumentation.rb +6 -5
- data/lib/ruby_llm/support/rescuable.rb +84 -0
- data/lib/ruby_llm/support/utils.rb +26 -3
- data/lib/ruby_llm/task.rb +126 -0
- data/lib/ruby_llm/test_helper.rb +129 -0
- data/lib/ruby_llm/thinking.rb +3 -3
- data/lib/ruby_llm/tokens.rb +52 -12
- data/lib/ruby_llm/tool.rb +339 -5
- data/lib/ruby_llm/tool_call/collection.rb +132 -0
- data/lib/ruby_llm/tool_call.rb +68 -6
- data/lib/ruby_llm/tools/provider_tools.rb +23 -0
- data/lib/ruby_llm/tools/search.rb +118 -0
- data/lib/ruby_llm/transcription/segment.rb +67 -0
- data/lib/ruby_llm/transcription/wav_audio.rb +2 -1
- data/lib/ruby_llm/transcription.rb +25 -49
- data/lib/ruby_llm/transcription_chunk.rb +9 -9
- data/lib/ruby_llm/transport/connection.rb +146 -33
- data/lib/ruby_llm/transport/error_middleware.rb +94 -49
- data/lib/ruby_llm/transport/event_stream_parser.rb +18 -4
- data/lib/ruby_llm/transport/json_response.rb +9 -3
- data/lib/ruby_llm/transport/log_formatter.rb +20 -0
- data/lib/ruby_llm/transport/release_request.rb +35 -0
- data/lib/ruby_llm/transport/websocket_connection.rb +26 -30
- data/lib/ruby_llm/uploaded_file.rb +15 -6
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm/video.rb +34 -10
- data/lib/ruby_llm/video_job.rb +55 -42
- data/lib/ruby_llm/web_source.rb +38 -0
- data/lib/ruby_llm/workflow.rb +2 -2
- data/lib/ruby_llm.rb +63 -25
- data/lib/tasks/ruby_llm/evaluations.rb +1 -1
- data/skills/rubyllm/SKILL.md +128 -54
- data/skills/rubyllm/references/cost-and-tracing.md +8 -0
- data/skills/rubyllm/references/files-code-and-context.md +6 -0
- data/skills/rubyllm/references/operations.md +8 -0
- data/skills/rubyllm/references/rails-chats.md +22 -0
- data/skills/rubyllm/references/tools-and-agents.md +11 -0
- metadata +219 -14
- data/lib/generators/ruby_llm/chat_ui/templates/helpers/messages_helper.rb.tt +0 -25
- data/lib/generators/ruby_llm/upgrade/templates/upgrade_ruby_llm_to_2_1.rb.tt +0 -78
data/lib/ruby_llm/agent.rb
CHANGED
|
@@ -9,14 +9,14 @@ module RubyLLM
|
|
|
9
9
|
# configured chats wherever they are needed.
|
|
10
10
|
#
|
|
11
11
|
# class SupportAgent < RubyLLM::Agent
|
|
12
|
-
# model "gpt-
|
|
12
|
+
# model "gpt-6-luna"
|
|
13
13
|
# instructions "You are a concise support assistant."
|
|
14
14
|
# tools SearchDocs, LookupAccount
|
|
15
15
|
# end
|
|
16
16
|
#
|
|
17
17
|
# SupportAgent.new.ask "How do I reset my API key?"
|
|
18
18
|
#
|
|
19
|
-
# ::chat returns a configured Chat. When ::
|
|
19
|
+
# ::chat returns a configured Chat. When ::chat_class names an ActiveRecord
|
|
20
20
|
# chat class, ::create, ::create!, and ::find return configured records of
|
|
21
21
|
# that class instead.
|
|
22
22
|
#
|
|
@@ -37,11 +37,14 @@ module RubyLLM
|
|
|
37
37
|
# Agent instances delegate Chat's conversation API (#ask, #complete,
|
|
38
38
|
# #with_tools, and so on) to the wrapped chat, which is available via
|
|
39
39
|
# #chat. Direct transcript replacement stays on the wrapped chat because
|
|
40
|
-
# Rails-backed chat models own their message association.
|
|
41
|
-
#
|
|
40
|
+
# Rails-backed chat models own their message association. Read the
|
|
41
|
+
# conversation through #messages.
|
|
42
42
|
class Agent
|
|
43
43
|
extend Forwardable
|
|
44
|
-
|
|
44
|
+
extend ModelOptions
|
|
45
|
+
singleton_class.prepend JoinCallbacks, Ownership, Callbacks, ToolConversion, ContextBudget
|
|
46
|
+
include Support::DeprecatedEnumerable
|
|
47
|
+
include Support::Rescuable
|
|
45
48
|
|
|
46
49
|
DUPED_INHERITED_CONFIG = {
|
|
47
50
|
:@chat_kwargs => {},
|
|
@@ -55,8 +58,7 @@ module RubyLLM
|
|
|
55
58
|
:@headers => {},
|
|
56
59
|
:@input_names => [],
|
|
57
60
|
:@fallbacks => [],
|
|
58
|
-
:@fallback_options => {}
|
|
59
|
-
:@rescue_handlers => []
|
|
61
|
+
:@fallback_options => {}
|
|
60
62
|
}.freeze
|
|
61
63
|
# Simple value options: a class-level getter/setter macro whose value the
|
|
62
64
|
# agent forwards to the matching Chat#with_* when it builds its chat.
|
|
@@ -65,42 +67,43 @@ module RubyLLM
|
|
|
65
67
|
UNRESOLVED_CONTEXT = Object.new.freeze
|
|
66
68
|
private_constant :UNRESOLVED_CONTEXT
|
|
67
69
|
|
|
68
|
-
# The chat operations an agent instance runs through its ::rescue_from
|
|
69
|
-
# handlers. Remaining delegated methods pass through untouched.
|
|
70
|
-
GUARDED_OPERATIONS = %i[ask say ask_later complete generate run_tools step count_tokens compact].freeze
|
|
71
|
-
|
|
72
70
|
# Chat methods that return the wrapped chat so calls can be chained there.
|
|
73
71
|
CHAINABLE_CHAT_DELEGATES = %i[
|
|
74
72
|
with_instructions with_tools with_mcp with_provider_tools with_tool_options with_model
|
|
75
|
-
with_temperature with_max_output_tokens with_thinking with_citations
|
|
76
|
-
with_end_user with_compaction with_caching with_context
|
|
77
|
-
with_headers with_schema with_fallbacks
|
|
78
|
-
before_request before_message after_message before_tool_call after_tool_result
|
|
79
|
-
before_fallback after_fallback
|
|
73
|
+
with_temperature with_max_output_tokens with_thinking with_citations with_context_budget
|
|
74
|
+
with_end_user with_owner with_compaction with_caching with_code_execution with_skills with_file_store with_context
|
|
75
|
+
with_provider_options with_headers with_schema with_fallbacks
|
|
76
|
+
before_request before_message after_message before_tool_call around_tool_call after_tool_result
|
|
77
|
+
after_tool_progress before_fallback after_fallback around_join after_file_write
|
|
80
78
|
convert_unsupported_attachments
|
|
81
|
-
cancel approve
|
|
79
|
+
cancel approve answer decline cache_until_here
|
|
82
80
|
].freeze
|
|
83
81
|
|
|
84
82
|
# Chat values and operations whose return values pass through unchanged.
|
|
85
83
|
PASSTHROUGH_CHAT_DELEGATES = %i[
|
|
86
|
-
model provider messages tools deferred_tools mcp provider_tools tool_options provider_options headers
|
|
87
|
-
concurrency caching citations compaction context end_user fallbacks thinking
|
|
88
|
-
|
|
89
|
-
|
|
84
|
+
model model_id provider messages tools deferred_tools mcp provider_tools tool_options provider_options headers
|
|
85
|
+
schema concurrency caching citations code_execution compaction context_budget context end_user fallbacks thinking
|
|
86
|
+
temperature max_output_tokens skills file_store
|
|
87
|
+
each complete? cancelled? waiting? pending_questions pending_tasks add_message add_completion tokens cost render
|
|
88
|
+
queue queued_messages withdraw join_queued_messages
|
|
90
89
|
].freeze
|
|
91
90
|
|
|
92
91
|
COPIED_INHERITED_CONFIG = (%i[
|
|
93
92
|
@thinking
|
|
94
93
|
@citations
|
|
94
|
+
@code_execution
|
|
95
|
+
@skills
|
|
96
|
+
@file_store
|
|
95
97
|
@end_user
|
|
96
98
|
@schema
|
|
97
99
|
@context
|
|
98
|
-
@
|
|
100
|
+
@context_budget
|
|
101
|
+
@chat_class
|
|
99
102
|
@tools_defer
|
|
100
103
|
@mcp_defer
|
|
101
104
|
] + PASSTHROUGH_OPTIONS.map { |option| :"@#{option}" }).freeze
|
|
102
105
|
private_constant :DUPED_INHERITED_CONFIG, :COPIED_INHERITED_CONFIG,
|
|
103
|
-
:PASSTHROUGH_OPTIONS, :THINKING_OPTIONS,
|
|
106
|
+
:PASSTHROUGH_OPTIONS, :THINKING_OPTIONS,
|
|
104
107
|
:CHAINABLE_CHAT_DELEGATES, :PASSTHROUGH_CHAT_DELEGATES
|
|
105
108
|
|
|
106
109
|
class << self
|
|
@@ -116,9 +119,17 @@ module RubyLLM
|
|
|
116
119
|
# available as methods. Called with no arguments, returns the
|
|
117
120
|
# configured chat keywords.
|
|
118
121
|
#
|
|
119
|
-
# model "gpt-
|
|
120
|
-
# model "gpt-
|
|
121
|
-
# model { quality == :high ? "gpt-
|
|
122
|
+
# model "gpt-6-luna"
|
|
123
|
+
# model "gpt-6.1-sol", provider: :openai, protocol: :responses
|
|
124
|
+
# model { quality == :high ? "gpt-6.1-sol" : "gpt-6-luna" }
|
|
125
|
+
#
|
|
126
|
+
# A +model:+ passed to ::chat, ::new, ::create, or ::create! replaces
|
|
127
|
+
# the declared model and its options, so the caller's model resolves
|
|
128
|
+
# on its own provider unless the caller also passes +provider:+. Options
|
|
129
|
+
# declared without a model stay. Later, ::find, ::new with +chat:+, and
|
|
130
|
+
# ::sync_instructions apply the options only to a record on the declared
|
|
131
|
+
# model, and always when a block declares it, since the block's choice
|
|
132
|
+
# is unknown until it runs.
|
|
122
133
|
#
|
|
123
134
|
# The block runs before the chat exists, so it can read inputs but not
|
|
124
135
|
# +chat+.
|
|
@@ -138,12 +149,19 @@ module RubyLLM
|
|
|
138
149
|
# tools SearchDocs, LookupAccount
|
|
139
150
|
# tools { [TodoTool.new(chat: chat)] }
|
|
140
151
|
#
|
|
141
|
-
# Pass +defer:+ as Chat#with_tools takes it
|
|
152
|
+
# Pass +defer:+ as Chat#with_tools takes it, or a lambda that decides
|
|
153
|
+
# when the chat is built:
|
|
142
154
|
#
|
|
143
155
|
# tools SearchDocs, LookupAccount, defer: true
|
|
156
|
+
# tools(defer: -> { many_tools? }) { connected_tools }
|
|
157
|
+
#
|
|
158
|
+
# With +defer:+ alone it applies to the tools a parent agent declared.
|
|
144
159
|
#
|
|
145
160
|
def tools(*tools, defer: nil, &block)
|
|
146
|
-
|
|
161
|
+
if tools.empty? && !block_given?
|
|
162
|
+
@tools_defer = defer unless defer.nil?
|
|
163
|
+
return @tools || []
|
|
164
|
+
end
|
|
147
165
|
|
|
148
166
|
@tools_defer = defer
|
|
149
167
|
@tools = block_given? ? block : tools.flatten
|
|
@@ -157,20 +175,26 @@ module RubyLLM
|
|
|
157
175
|
# mcp Files
|
|
158
176
|
# mcp { [Linear.new(user: user), Files] }
|
|
159
177
|
#
|
|
160
|
-
# Pass +defer:+ as Chat#with_mcp takes it
|
|
178
|
+
# Pass +defer:+ as Chat#with_mcp takes it, or a lambda that decides
|
|
179
|
+
# when the chat is built:
|
|
161
180
|
#
|
|
162
181
|
# mcp GitHub, defer: true
|
|
163
182
|
#
|
|
183
|
+
# With +defer:+ alone it applies to the servers a parent agent declared.
|
|
184
|
+
#
|
|
164
185
|
def mcp(*servers, defer: nil, &block)
|
|
165
|
-
|
|
186
|
+
if servers.empty? && !block_given?
|
|
187
|
+
@mcp_defer = defer unless defer.nil?
|
|
188
|
+
return @mcp || []
|
|
189
|
+
end
|
|
166
190
|
|
|
167
191
|
@mcp_defer = defer
|
|
168
192
|
@mcp = block_given? ? block : servers.flatten
|
|
169
193
|
end
|
|
170
194
|
|
|
171
195
|
# Sets how chats this agent builds use their tools, applied via
|
|
172
|
-
# Chat#with_tool_options. Accepts +choice:+, +calls:+,
|
|
173
|
-
# +
|
|
196
|
+
# Chat#with_tool_options. Accepts +choice:+, +calls:+, +concurrency:+,
|
|
197
|
+
# and +rounds:+. A block defers evaluation until the chat is built.
|
|
174
198
|
# Called with no arguments, returns the configured options.
|
|
175
199
|
#
|
|
176
200
|
# tool_options choice: :required, calls: :one
|
|
@@ -183,11 +207,13 @@ module RubyLLM
|
|
|
183
207
|
|
|
184
208
|
# Enables provider-executed tools for chats this agent builds, applied
|
|
185
209
|
# via Chat#with_provider_tools. Accepts the same aliases, options, and
|
|
186
|
-
# raw Hashes; a block defers evaluation until the chat is built
|
|
210
|
+
# raw Hashes; a block defers evaluation until the chat is built and
|
|
211
|
+
# returns a list, or a Hash of aliases and options like the keywords.
|
|
187
212
|
# Called with no arguments, returns the declared entries.
|
|
188
213
|
#
|
|
189
214
|
# provider_tools :web_search
|
|
190
215
|
# provider_tools web_search: { allowed_domains: ["ruby-lang.org"] }
|
|
216
|
+
# provider_tools { { web_search: { max_uses: max_searches } } }
|
|
191
217
|
#
|
|
192
218
|
def provider_tools(*tools, **tools_with_options, &block)
|
|
193
219
|
return @provider_tools || [] if tools.empty? && tools_with_options.empty? && !block_given?
|
|
@@ -286,6 +312,89 @@ module RubyLLM
|
|
|
286
312
|
@compaction = options
|
|
287
313
|
end
|
|
288
314
|
|
|
315
|
+
# Keeps tool results within shares of the model's context window in
|
|
316
|
+
# chats this agent builds, applied via Chat#with_context_budget, in
|
|
317
|
+
# place of Configuration#context_budget. Pass +nil+ for no budget.
|
|
318
|
+
#
|
|
319
|
+
# context_budget per_result: 0.25, per_request: 0.7
|
|
320
|
+
# context_budget window: 128_000, per_request: 0.7
|
|
321
|
+
# context_budget nil
|
|
322
|
+
#
|
|
323
|
+
# A block is evaluated for each chat, with the agent's inputs and
|
|
324
|
+
# +chat+ in scope, and returns the options, or +nil+ for no budget.
|
|
325
|
+
#
|
|
326
|
+
# context_budget { { window: model_window, per_request: 0.7 } if model_window }
|
|
327
|
+
#
|
|
328
|
+
def context_budget(budget = {}, **, &) = (@context_budget = context_budget_option(budget, **, &))
|
|
329
|
+
|
|
330
|
+
# Lets chats this agent builds run code in the configured sandbox,
|
|
331
|
+
# applied via Chat#with_code_execution. Accepts the same options as
|
|
332
|
+
# keywords or a Hash. Pass +false+ to disable it. Passing +nil+ or a
|
|
333
|
+
# block raises ArgumentError.
|
|
334
|
+
#
|
|
335
|
+
# code_execution
|
|
336
|
+
# code_execution false
|
|
337
|
+
# code_execution languages: [:ruby, :python], gems: %w[prawn]
|
|
338
|
+
# code_execution sandbox: ReadOnlySandbox, escalate_to: [WorkspaceSandbox]
|
|
339
|
+
# code_execution mounts: -> { { "templates" => chat.templates_dir } }
|
|
340
|
+
#
|
|
341
|
+
# A +mounts+ lambda runs before each run, with +chat+ and the
|
|
342
|
+
# agent's inputs in scope, like the agent's other lambdas.
|
|
343
|
+
def code_execution(enabled = true, **options) # rubocop:disable Style/OptionalBooleanParameter
|
|
344
|
+
raise ArgumentError, 'code_execution takes no block. Pass a lambda as mounts: for each run' if block_given?
|
|
345
|
+
|
|
346
|
+
@code_execution = CodeExecution.options(enabled, options)
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
# Attaches a file store to chats this agent builds, with the tools to
|
|
350
|
+
# work with it, applied via Chat#with_file_store, which takes the
|
|
351
|
+
# same arguments. A +store+ lambda runs once for each chat, with
|
|
352
|
+
# +chat+ and the agent's inputs in scope, like the agent's other
|
|
353
|
+
# lambdas. Pass +nil+ to remove the store a parent agent declared. A
|
|
354
|
+
# block raises ArgumentError: pass the lambda as the store instead.
|
|
355
|
+
#
|
|
356
|
+
# file_store
|
|
357
|
+
# file_store inline_limit: 20_000, tools: :read
|
|
358
|
+
# file_store :memory
|
|
359
|
+
# file_store -> { "tmp/chats/#{chat.id}" }
|
|
360
|
+
# file_store nil
|
|
361
|
+
#
|
|
362
|
+
# Each chat gets a store of its own: without +store+, a new
|
|
363
|
+
# FileStore::Memory, or for a persisted chat, its files in Active
|
|
364
|
+
# Storage, kept for the chat's owner (see ::owner). A folder path on
|
|
365
|
+
# its own is shared by every chat, so use a lambda to give each chat
|
|
366
|
+
# its own folder.
|
|
367
|
+
#
|
|
368
|
+
# :call-seq: file_store(store = default, inline_limit: 10_000, tools: %i[read write edit])
|
|
369
|
+
def file_store(store = Chat::FileReferences::DEFAULT_STORE, inline_limit: Chat::FileReferences::INLINE_LIMIT,
|
|
370
|
+
tools: FileStore::Tools::DEFAULT)
|
|
371
|
+
raise ArgumentError, 'file_store takes no block. Pass the store as a lambda instead' if block_given?
|
|
372
|
+
return @file_store = false if store.nil?
|
|
373
|
+
|
|
374
|
+
Chat::FileReferences.validate(store, inline_limit:, tools:, store_lambda: true)
|
|
375
|
+
@file_store = { store:, inline_limit:, tools: }.freeze
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
# Gives chats this agent builds the skills named, applied via
|
|
379
|
+
# Chat#with_skills when the chat is built. Symbols name folders under
|
|
380
|
+
# Skill.roots, Strings and Pathnames are paths to skill folders,
|
|
381
|
+
# +true+ is every skill under the roots, and +false+ removes the
|
|
382
|
+
# skills a parent agent declared. An agent without a +skills+
|
|
383
|
+
# declaration has no skills. Called with no arguments, returns the
|
|
384
|
+
# declaration: the names, +true+, +false+, or +nil+. A block raises
|
|
385
|
+
# ArgumentError.
|
|
386
|
+
#
|
|
387
|
+
# skills :invoices, :refunds
|
|
388
|
+
# skills true
|
|
389
|
+
# skills false
|
|
390
|
+
#
|
|
391
|
+
def skills(*skills)
|
|
392
|
+
raise ArgumentError, 'skills takes no block. Name the skills, or pass true for every skill' if block_given?
|
|
393
|
+
return @skills if skills.empty?
|
|
394
|
+
|
|
395
|
+
@skills = Skill.selection(skills)
|
|
396
|
+
end
|
|
397
|
+
|
|
289
398
|
# Sets the safety identifier for chats this agent builds, applied via
|
|
290
399
|
# Chat#with_end_user. A block defers evaluation until the
|
|
291
400
|
# chat is built, so the id can come from the agent's inputs. Called
|
|
@@ -294,11 +403,21 @@ module RubyLLM
|
|
|
294
403
|
# end_user "tenant-42"
|
|
295
404
|
# end_user { workspace.public_id }
|
|
296
405
|
#
|
|
297
|
-
def end_user(value = nil, &block)
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
406
|
+
def end_user(value = nil, &block) = value.nil? && !block ? @end_user : (@end_user = block || value)
|
|
407
|
+
|
|
408
|
+
# Sets whoever the usage of chats this agent builds or finds is
|
|
409
|
+
# attributed to, applied via Chat#with_owner. A block defers
|
|
410
|
+
# evaluation until the chat is built, so the owner can come from the
|
|
411
|
+
# agent's inputs or, for a persisted chat, from +chat+. A persisted
|
|
412
|
+
# chat keeps the files of its Active Storage store for the owner
|
|
413
|
+
# too, and the owner wins over the association its
|
|
414
|
+
# <tt>acts_as_chat owner:</tt> names. Subclasses inherit it. Called
|
|
415
|
+
# with no arguments, returns the configured value.
|
|
416
|
+
#
|
|
417
|
+
# owner { account }
|
|
418
|
+
# owner { chat.user }
|
|
419
|
+
#
|
|
420
|
+
def owner(value = nil, &block) = value.nil? && !block ? @owner : (@owner = block || value)
|
|
302
421
|
|
|
303
422
|
# Enables citations for chats this agent builds, applied via
|
|
304
423
|
# Chat#with_citations. Pass +false+ to disable them.
|
|
@@ -320,13 +439,18 @@ module RubyLLM
|
|
|
320
439
|
#
|
|
321
440
|
# caching
|
|
322
441
|
# caching false
|
|
442
|
+
# caching cache
|
|
323
443
|
# caching ttl: "1h"
|
|
324
444
|
# caching { { ttl: workspace.cache_ttl } }
|
|
325
445
|
#
|
|
326
446
|
def caching(enabled = true, **options, &block) # rubocop:disable Metrics/PerceivedComplexity, Style/OptionalBooleanParameter
|
|
327
447
|
return caching(**enabled.transform_keys(&:to_sym), **options, &block) if enabled.is_a?(Hash)
|
|
448
|
+
return caching(id: enabled, **options, &block) if enabled.is_a?(CachedContent)
|
|
449
|
+
|
|
450
|
+
unless [true, false].include?(enabled)
|
|
451
|
+
raise ArgumentError, 'caching accepts false, a CachedContent, or caching options'
|
|
452
|
+
end
|
|
328
453
|
|
|
329
|
-
raise ArgumentError, 'caching accepts false or caching options' unless [true, false].include?(enabled)
|
|
330
454
|
raise ArgumentError, 'caching accepts options or a block, not both' if options.any? && block
|
|
331
455
|
raise ArgumentError, 'caching false does not accept options or a block' if !enabled && (options.any? || block)
|
|
332
456
|
|
|
@@ -375,17 +499,19 @@ module RubyLLM
|
|
|
375
499
|
|
|
376
500
|
# Sets fallback models for chats this agent builds, applied via
|
|
377
501
|
# Chat#with_fallbacks. Called with no arguments, returns the
|
|
378
|
-
# configured models.
|
|
502
|
+
# configured models. Keep the default +on:+: providers signal
|
|
503
|
+
# overload as HTTP 529 or 503, and the default covers both.
|
|
379
504
|
#
|
|
380
505
|
# fallbacks "gpt-4.1-mini", "claude-haiku-4-5"
|
|
381
|
-
# fallbacks "gpt-4.1-mini", on: [RubyLLM::
|
|
506
|
+
# fallbacks "gpt-4.1-mini", on: [*RubyLLM::Fallback::DEFAULT_ERRORS, RubyLLM::ContextLengthExceededError]
|
|
507
|
+
# fallbacks drop_unsupported: true
|
|
382
508
|
#
|
|
383
|
-
def fallbacks(*models,
|
|
384
|
-
return @fallbacks || [] if models.empty? &&
|
|
385
|
-
raise ArgumentError, 'To set fallback options, provide at least one fallback model' if
|
|
509
|
+
def fallbacks(*models, on: Fallback::DEFAULT_ERRORS, drop_unsupported: false)
|
|
510
|
+
return @fallbacks || [] if (bare = models.empty? && !drop_unsupported) && on.equal?(Fallback::DEFAULT_ERRORS)
|
|
511
|
+
raise ArgumentError, 'To set fallback options, provide at least one fallback model' if bare
|
|
386
512
|
|
|
387
513
|
@fallbacks = models.flatten.compact
|
|
388
|
-
@fallback_options =
|
|
514
|
+
@fallback_options = { on:, drop_unsupported: }
|
|
389
515
|
end
|
|
390
516
|
|
|
391
517
|
def fallback_options
|
|
@@ -425,13 +551,18 @@ module RubyLLM
|
|
|
425
551
|
# ::sync_instructions). Accepts the class or its name as a string.
|
|
426
552
|
# Called with no argument, returns the configured value.
|
|
427
553
|
#
|
|
428
|
-
#
|
|
554
|
+
# chat_class Chat
|
|
429
555
|
#
|
|
430
|
-
def
|
|
431
|
-
return @
|
|
556
|
+
def chat_class(value = nil)
|
|
557
|
+
return @chat_class if value.nil?
|
|
558
|
+
|
|
559
|
+
@chat_class = value
|
|
560
|
+
remove_instance_variable(:@resolved_chat_class) if instance_variable_defined?(:@resolved_chat_class)
|
|
561
|
+
end
|
|
432
562
|
|
|
433
|
-
|
|
434
|
-
|
|
563
|
+
def chat_model(value = nil) # :nodoc:
|
|
564
|
+
RubyLLM.deprecator.deprecate('Agent.chat_model', replacement: 'Agent.chat_class', removal: '3.0')
|
|
565
|
+
chat_class(value)
|
|
435
566
|
end
|
|
436
567
|
|
|
437
568
|
# Declares named runtime inputs. Matching keyword arguments passed to
|
|
@@ -447,57 +578,6 @@ module RubyLLM
|
|
|
447
578
|
@input_names = names.flatten.map(&:to_sym)
|
|
448
579
|
end
|
|
449
580
|
|
|
450
|
-
# Registers a handler for exceptions raised by the chat operations of
|
|
451
|
-
# this agent's instances: #ask, #say, #ask_later, #complete,
|
|
452
|
-
# #generate, #run_tools, #step, #count_tokens, and #compact. Name the
|
|
453
|
-
# handler with +with:+ or pass a block; either runs on the agent instance,
|
|
454
|
-
# so the agent's #chat, inputs, and class name are available for instrumentation.
|
|
455
|
-
#
|
|
456
|
-
# class ApplicationAgent < RubyLLM::Agent
|
|
457
|
-
# rescue_from RubyLLM::RateLimitError, Faraday::TimeoutError, with: :handle_transient
|
|
458
|
-
# rescue_from RubyLLM::BadRequestError do |error|
|
|
459
|
-
# error_tracker.notify(error)
|
|
460
|
-
# raise
|
|
461
|
-
# end
|
|
462
|
-
#
|
|
463
|
-
# private
|
|
464
|
-
#
|
|
465
|
-
# def handle_transient(error)
|
|
466
|
-
# metrics.increment("llm.api_error", type: "transient")
|
|
467
|
-
# raise
|
|
468
|
-
# end
|
|
469
|
-
# end
|
|
470
|
-
#
|
|
471
|
-
# Handlers are searched in reverse declaration order, so the last
|
|
472
|
-
# matching one wins. Re-raise inside a handler to let the caller see
|
|
473
|
-
# the exception; otherwise the handler's return value becomes the
|
|
474
|
-
# operation's return value. Exceptions no handler matches are
|
|
475
|
-
# re-raised. Subclasses inherit the handlers declared when they are
|
|
476
|
-
# defined.
|
|
477
|
-
#
|
|
478
|
-
# Exception classes may be named as Strings, which defers constant
|
|
479
|
-
# lookup until an exception is raised.
|
|
480
|
-
def rescue_from(*exception_classes, with: nil, &block)
|
|
481
|
-
raise ArgumentError, 'rescue_from needs a handler: pass with: or a block' unless with || block
|
|
482
|
-
raise ArgumentError, 'rescue_from takes with: or a block, not both' if with && block
|
|
483
|
-
|
|
484
|
-
exception_classes.flatten.each do |exception_class|
|
|
485
|
-
rescue_handlers << [rescue_handler_key(exception_class), with || block]
|
|
486
|
-
end
|
|
487
|
-
end
|
|
488
|
-
|
|
489
|
-
def rescue_handlers # :nodoc:
|
|
490
|
-
@rescue_handlers ||= []
|
|
491
|
-
end
|
|
492
|
-
|
|
493
|
-
def rescue_handler_for(exception) # :nodoc:
|
|
494
|
-
rescue_handlers.reverse_each do |class_name, handler|
|
|
495
|
-
exception_class = rescue_handler_class(class_name)
|
|
496
|
-
return handler if exception_class && exception.is_a?(exception_class)
|
|
497
|
-
end
|
|
498
|
-
nil
|
|
499
|
-
end
|
|
500
|
-
|
|
501
581
|
def chat_kwargs # :nodoc:
|
|
502
582
|
@chat_kwargs || {}
|
|
503
583
|
end
|
|
@@ -511,16 +591,9 @@ module RubyLLM
|
|
|
511
591
|
evaluate(value, runtime_context(chat: nil, inputs:))
|
|
512
592
|
end
|
|
513
593
|
|
|
514
|
-
def resolved_chat_kwargs(inputs: {}) # :nodoc:
|
|
515
|
-
kwargs = chat_kwargs
|
|
516
|
-
return kwargs unless kwargs[:model].is_a?(Proc)
|
|
517
|
-
|
|
518
|
-
kwargs.merge(model: evaluate(kwargs[:model], runtime_context(chat: nil, inputs: inputs)))
|
|
519
|
-
end
|
|
520
|
-
|
|
521
594
|
def build_chat(inputs:, options:, resolved_context: UNRESOLVED_CONTEXT) # :nodoc:
|
|
522
595
|
context_value = resolved_context.equal?(UNRESOLVED_CONTEXT) ? self.resolved_context(inputs:) : resolved_context
|
|
523
|
-
(context_value || RubyLLM).chat(**resolved_chat_kwargs(inputs
|
|
596
|
+
(context_value || RubyLLM).chat(**resolved_chat_kwargs(inputs:, options:))
|
|
524
597
|
end
|
|
525
598
|
|
|
526
599
|
# Builds a Chat configured with this agent's declarations and returns
|
|
@@ -538,13 +611,13 @@ module RubyLLM
|
|
|
538
611
|
chat
|
|
539
612
|
end
|
|
540
613
|
|
|
541
|
-
# Creates a ::
|
|
614
|
+
# Creates a ::chat_class record, applies this agent's configuration to
|
|
542
615
|
# it, and returns it. Keywords matching declared ::inputs become
|
|
543
616
|
# runtime inputs; the rest are forwarded to the model's +create+.
|
|
544
617
|
#
|
|
545
618
|
# chat = WorkAssistant.create(user: current_user)
|
|
546
619
|
#
|
|
547
|
-
# Raises ArgumentError if ::
|
|
620
|
+
# Raises ArgumentError if ::chat_class is not configured.
|
|
548
621
|
def create(**)
|
|
549
622
|
with_rails_chat_record(:create, **)
|
|
550
623
|
end
|
|
@@ -558,38 +631,37 @@ module RubyLLM
|
|
|
558
631
|
with_rails_chat_record(:create!, **)
|
|
559
632
|
end
|
|
560
633
|
|
|
561
|
-
# Finds the ::
|
|
634
|
+
# Finds the ::chat_class record with +id+ and applies this agent's
|
|
562
635
|
# configuration at runtime, without persisting instructions. Returns
|
|
563
636
|
# the record.
|
|
564
637
|
#
|
|
565
638
|
# chat = WorkAssistant.find(params[:id])
|
|
566
639
|
#
|
|
567
|
-
# Raises ArgumentError if ::
|
|
640
|
+
# Raises ArgumentError if ::chat_class is not configured.
|
|
568
641
|
def find(id, **kwargs)
|
|
569
|
-
raise ArgumentError, '
|
|
642
|
+
raise ArgumentError, 'chat_class must be configured to use find' unless resolved_chat_class
|
|
570
643
|
|
|
571
644
|
input_values, = partition_inputs(kwargs)
|
|
572
|
-
record =
|
|
645
|
+
record = resolved_chat_class.find(id)
|
|
573
646
|
apply_configuration(record, input_values:, persist_instructions: false)
|
|
574
647
|
|
|
575
648
|
record
|
|
576
649
|
end
|
|
577
650
|
|
|
578
651
|
# Re-renders this agent's instructions and persists them on the given
|
|
579
|
-
# ::
|
|
652
|
+
# ::chat_class record (or the record found by that id). Keywords
|
|
580
653
|
# matching declared ::inputs become runtime inputs. Returns the
|
|
581
654
|
# record.
|
|
582
655
|
#
|
|
583
656
|
# WorkAssistant.sync_instructions(chat)
|
|
584
657
|
#
|
|
585
|
-
# Raises ArgumentError if ::
|
|
658
|
+
# Raises ArgumentError if ::chat_class is not configured.
|
|
586
659
|
def sync_instructions(chat_or_id, **kwargs)
|
|
587
|
-
raise ArgumentError, '
|
|
660
|
+
raise ArgumentError, 'chat_class must be configured to use sync_instructions' unless resolved_chat_class
|
|
588
661
|
|
|
589
662
|
input_values, = partition_inputs(kwargs)
|
|
590
|
-
record = chat_or_id.is_a?(
|
|
591
|
-
|
|
592
|
-
apply_protocol(record)
|
|
663
|
+
record = chat_or_id.is_a?(resolved_chat_class) ? chat_or_id : resolved_chat_class.find(chat_or_id)
|
|
664
|
+
apply_chat_options(record)
|
|
593
665
|
runtime = runtime_context(chat: record, inputs: input_values)
|
|
594
666
|
apply_context(record, runtime)
|
|
595
667
|
apply_instructions(
|
|
@@ -627,10 +699,12 @@ module RubyLLM
|
|
|
627
699
|
chat,
|
|
628
700
|
input_values:,
|
|
629
701
|
persist_instructions:,
|
|
630
|
-
resolved_context: UNRESOLVED_CONTEXT
|
|
702
|
+
resolved_context: UNRESOLVED_CONTEXT,
|
|
703
|
+
agent: nil,
|
|
704
|
+
chat_options: {}
|
|
631
705
|
)
|
|
632
706
|
runtime = runtime_context(chat:, inputs: input_values)
|
|
633
|
-
apply_chat_options(chat)
|
|
707
|
+
apply_chat_options(chat, chat_options)
|
|
634
708
|
apply_context(chat, runtime, resolved_context:)
|
|
635
709
|
apply_instructions(chat, runtime, inputs: input_values, persist: persist_instructions)
|
|
636
710
|
apply_tools(chat, runtime)
|
|
@@ -640,10 +714,17 @@ module RubyLLM
|
|
|
640
714
|
apply_end_user(chat, runtime)
|
|
641
715
|
apply_caching(chat, runtime)
|
|
642
716
|
apply_compaction(chat)
|
|
717
|
+
apply_code_execution(chat, runtime)
|
|
718
|
+
apply_skills(chat)
|
|
719
|
+
apply_file_store(chat, runtime)
|
|
720
|
+
apply_tool_options(chat, runtime)
|
|
643
721
|
apply_provider_options(chat, runtime)
|
|
644
722
|
apply_headers(chat, runtime)
|
|
645
723
|
apply_schema(chat, runtime)
|
|
646
724
|
apply_fallbacks(chat)
|
|
725
|
+
apply_rescue_handlers(chat, agent, input_values)
|
|
726
|
+
target = chat.respond_to?(:to_llm) ? chat.to_llm : chat
|
|
727
|
+
target.agent_class = self if target.respond_to?(:agent_class=)
|
|
647
728
|
end
|
|
648
729
|
|
|
649
730
|
private
|
|
@@ -666,28 +747,12 @@ module RubyLLM
|
|
|
666
747
|
raise ArgumentError, "thinking accepts #{THINKING_OPTIONS.join(', ')}, got #{unsupported.join(', ')}"
|
|
667
748
|
end
|
|
668
749
|
|
|
669
|
-
def rescue_handler_key(exception_class)
|
|
670
|
-
case exception_class
|
|
671
|
-
when Module then exception_class.name || exception_class
|
|
672
|
-
when String then exception_class
|
|
673
|
-
else raise ArgumentError, "#{exception_class.inspect} is not an exception class or its name"
|
|
674
|
-
end
|
|
675
|
-
end
|
|
676
|
-
|
|
677
|
-
def rescue_handler_class(class_name)
|
|
678
|
-
return class_name if class_name.is_a?(Module)
|
|
679
|
-
|
|
680
|
-
Object.const_get(class_name)
|
|
681
|
-
rescue NameError
|
|
682
|
-
nil
|
|
683
|
-
end
|
|
684
|
-
|
|
685
750
|
def copy_inherited_config_to(subclass)
|
|
686
751
|
subclass.instance_variable_set(:@inherited_instruction_declarations, instruction_declarations.dup)
|
|
687
752
|
|
|
688
753
|
DUPED_INHERITED_CONFIG.each do |ivar, default|
|
|
689
754
|
value = instance_variable_defined?(ivar) ? instance_variable_get(ivar) : default
|
|
690
|
-
subclass.instance_variable_set(ivar, value.
|
|
755
|
+
subclass.instance_variable_set(ivar, value.dup)
|
|
691
756
|
end
|
|
692
757
|
|
|
693
758
|
COPIED_INHERITED_CONFIG.each do |ivar|
|
|
@@ -696,13 +761,13 @@ module RubyLLM
|
|
|
696
761
|
end
|
|
697
762
|
|
|
698
763
|
def with_rails_chat_record(method_name, **kwargs)
|
|
699
|
-
raise ArgumentError, '
|
|
764
|
+
raise ArgumentError, 'chat_class must be configured to use create/create!' unless resolved_chat_class
|
|
700
765
|
|
|
701
766
|
input_values, chat_options = partition_inputs(kwargs)
|
|
702
|
-
record =
|
|
703
|
-
method_name, **resolved_chat_kwargs(inputs: input_values
|
|
767
|
+
record = resolved_chat_class.public_send(
|
|
768
|
+
method_name, **resolved_chat_kwargs(inputs: input_values, options: chat_options)
|
|
704
769
|
)
|
|
705
|
-
apply_configuration(record, input_values:, persist_instructions: true) if record
|
|
770
|
+
apply_configuration(record, input_values:, persist_instructions: true, chat_options:) if record&.persisted?
|
|
706
771
|
record
|
|
707
772
|
end
|
|
708
773
|
|
|
@@ -712,7 +777,13 @@ module RubyLLM
|
|
|
712
777
|
end
|
|
713
778
|
|
|
714
779
|
def apply_instructions(chat, runtime, inputs:, persist:, persistent_only: false)
|
|
715
|
-
|
|
780
|
+
instructions = resolved_instructions(chat, runtime, inputs:, persist:, persistent_only:)
|
|
781
|
+
instructions.reject! { |_, options| options[:persist] } if instructions_persisted?(chat, instructions)
|
|
782
|
+
instructions.each { |value, options| chat.with_instructions(value, **options) }
|
|
783
|
+
end
|
|
784
|
+
|
|
785
|
+
def resolved_instructions(chat, runtime, inputs:, persist:, persistent_only:)
|
|
786
|
+
instructions_config.filter_map do |declaration|
|
|
716
787
|
next if persistent_only && !declaration[:persist]
|
|
717
788
|
|
|
718
789
|
value = resolved_instruction_value(declaration, chat, runtime, inputs:)
|
|
@@ -723,10 +794,22 @@ module RubyLLM
|
|
|
723
794
|
cache_until_here: declaration[:cache_until_here]
|
|
724
795
|
}
|
|
725
796
|
options[:persist] = persist && declaration[:persist] if rails_chat_record?(chat)
|
|
726
|
-
|
|
797
|
+
[value, options]
|
|
727
798
|
end
|
|
728
799
|
end
|
|
729
800
|
|
|
801
|
+
# Applying an agent to a record on every request would otherwise
|
|
802
|
+
# rewrite the same system rows each time, behind the conversation.
|
|
803
|
+
def instructions_persisted?(chat, instructions)
|
|
804
|
+
persistent = instructions.select { |_, options| options[:persist] }
|
|
805
|
+
replacement = persistent.rindex { |_, options| !options[:append] }
|
|
806
|
+
return false unless replacement
|
|
807
|
+
|
|
808
|
+
chat.system_instructions_persisted?(
|
|
809
|
+
persistent.drop(replacement).map { |value, options| [value, options[:cache_until_here]] }
|
|
810
|
+
)
|
|
811
|
+
end
|
|
812
|
+
|
|
730
813
|
# An empty prompt file, such as the one the agent generator writes,
|
|
731
814
|
# means no instructions rather than an empty system message.
|
|
732
815
|
def blank_instruction?(value)
|
|
@@ -735,16 +818,18 @@ module RubyLLM
|
|
|
735
818
|
|
|
736
819
|
def apply_tools(chat, runtime)
|
|
737
820
|
tools_to_apply = Array(evaluate(tools, runtime)).compact
|
|
738
|
-
chat.with_tools(*tools_to_apply, defer: @tools_defer) if tools_to_apply.any?
|
|
821
|
+
chat.with_tools(*tools_to_apply, defer: evaluate(@tools_defer, runtime)) if tools_to_apply.any?
|
|
739
822
|
|
|
740
823
|
servers = Array(evaluate(mcp, runtime)).compact
|
|
741
|
-
chat.with_mcp(*servers, defer: @mcp_defer) if servers.any?
|
|
824
|
+
chat.with_mcp(*servers, defer: evaluate(@mcp_defer, runtime)) if servers.any?
|
|
825
|
+
|
|
826
|
+
provider_tools_to_apply = RubyLLM::Tools::ProviderTools.entries(evaluate(provider_tools, runtime))
|
|
827
|
+
chat.with_provider_tools(*provider_tools_to_apply) if provider_tools_to_apply.any?
|
|
828
|
+
end
|
|
742
829
|
|
|
830
|
+
def apply_tool_options(chat, runtime)
|
|
743
831
|
options = evaluate(tool_options, runtime)
|
|
744
832
|
chat.with_tool_options(**options) if options && !options.empty?
|
|
745
|
-
|
|
746
|
-
provider_tools_to_apply = Array(evaluate(provider_tools, runtime)).compact
|
|
747
|
-
chat.with_provider_tools(*provider_tools_to_apply) if provider_tools_to_apply.any?
|
|
748
833
|
end
|
|
749
834
|
|
|
750
835
|
def apply_passthrough_options(chat)
|
|
@@ -782,6 +867,33 @@ module RubyLLM
|
|
|
782
867
|
@compaction == false ? chat.with_compaction(false) : chat.with_compaction(**@compaction)
|
|
783
868
|
end
|
|
784
869
|
|
|
870
|
+
def apply_code_execution(chat, runtime)
|
|
871
|
+
return if @code_execution.nil?
|
|
872
|
+
return chat.with_code_execution(false) if @code_execution == false
|
|
873
|
+
|
|
874
|
+
options = @code_execution
|
|
875
|
+
mounts = options[:mounts]
|
|
876
|
+
options = options.merge(mounts: -> { evaluate(mounts, runtime) }) if mounts.is_a?(Proc)
|
|
877
|
+
chat.with_code_execution(**options)
|
|
878
|
+
end
|
|
879
|
+
|
|
880
|
+
def apply_file_store(chat, runtime)
|
|
881
|
+
return if @file_store.nil?
|
|
882
|
+
return chat.with_file_store(nil) if @file_store == false
|
|
883
|
+
|
|
884
|
+
store = @file_store[:store]
|
|
885
|
+
options = @file_store.except(:store).transform_values { |value| evaluate(value, runtime) }
|
|
886
|
+
return chat.with_file_store(**options) if store.equal?(Chat::FileReferences::DEFAULT_STORE)
|
|
887
|
+
|
|
888
|
+
chat.with_file_store(evaluate(store, runtime), **options)
|
|
889
|
+
end
|
|
890
|
+
|
|
891
|
+
def apply_skills(chat)
|
|
892
|
+
return if @skills.nil?
|
|
893
|
+
|
|
894
|
+
chat.with_skills(*Array(@skills))
|
|
895
|
+
end
|
|
896
|
+
|
|
785
897
|
def apply_provider_options(chat, runtime)
|
|
786
898
|
value = evaluate(provider_options, runtime)
|
|
787
899
|
chat.with_provider_options(**value) if value && !value.empty?
|
|
@@ -798,7 +910,13 @@ module RubyLLM
|
|
|
798
910
|
end
|
|
799
911
|
|
|
800
912
|
def apply_fallbacks(chat)
|
|
801
|
-
chat.with_fallbacks(*fallbacks, **fallback_options) if fallbacks.any?
|
|
913
|
+
chat.with_fallbacks(*fallbacks, **fallback_options) if fallbacks.any? || fallback_options[:drop_unsupported]
|
|
914
|
+
end
|
|
915
|
+
|
|
916
|
+
def apply_rescue_handlers(chat, agent, input_values)
|
|
917
|
+
return unless chat.is_a?(Chat::Rescuable)
|
|
918
|
+
|
|
919
|
+
chat.rescuer = rescue_handlers? ? agent || allocate.bind(chat, input_values) : nil
|
|
802
920
|
end
|
|
803
921
|
|
|
804
922
|
def resolved_schema_value(runtime)
|
|
@@ -809,27 +927,6 @@ module RubyLLM
|
|
|
809
927
|
Schematist::Schema.create(&value)
|
|
810
928
|
end
|
|
811
929
|
|
|
812
|
-
def apply_chat_options(chat)
|
|
813
|
-
apply_assume_model_exists(chat)
|
|
814
|
-
apply_protocol(chat)
|
|
815
|
-
end
|
|
816
|
-
|
|
817
|
-
def apply_assume_model_exists(chat_object)
|
|
818
|
-
return unless chat_kwargs.key?(:assume_model_exists) &&
|
|
819
|
-
resolved_chat_model &&
|
|
820
|
-
chat_object.is_a?(resolved_chat_model)
|
|
821
|
-
|
|
822
|
-
chat_object.assume_model_exists = chat_kwargs[:assume_model_exists]
|
|
823
|
-
end
|
|
824
|
-
|
|
825
|
-
def apply_protocol(chat_object)
|
|
826
|
-
return unless chat_kwargs.key?(:protocol) &&
|
|
827
|
-
resolved_chat_model &&
|
|
828
|
-
chat_object.is_a?(resolved_chat_model)
|
|
829
|
-
|
|
830
|
-
chat_object.protocol = chat_kwargs[:protocol]
|
|
831
|
-
end
|
|
832
|
-
|
|
833
930
|
def evaluate(value, runtime)
|
|
834
931
|
value.is_a?(Proc) ? runtime.instance_exec(&value) : value
|
|
835
932
|
end
|
|
@@ -860,7 +957,7 @@ module RubyLLM
|
|
|
860
957
|
end
|
|
861
958
|
|
|
862
959
|
def rails_chat_record?(chat)
|
|
863
|
-
|
|
960
|
+
resolved_chat_class && chat.is_a?(resolved_chat_class)
|
|
864
961
|
end
|
|
865
962
|
|
|
866
963
|
def default_instructions_prompt_exists?
|
|
@@ -898,12 +995,12 @@ module RubyLLM
|
|
|
898
995
|
Support::Utils.underscore(class_name.gsub('::', '/')).tr('-', '_')
|
|
899
996
|
end
|
|
900
997
|
|
|
901
|
-
def
|
|
902
|
-
return @
|
|
998
|
+
def resolved_chat_class
|
|
999
|
+
return @resolved_chat_class if defined?(@resolved_chat_class)
|
|
903
1000
|
|
|
904
|
-
@
|
|
905
|
-
when String then Object.const_get(@
|
|
906
|
-
else @
|
|
1001
|
+
@resolved_chat_class = case @chat_class
|
|
1002
|
+
when String then Object.const_get(@chat_class)
|
|
1003
|
+
else @chat_class
|
|
907
1004
|
end
|
|
908
1005
|
end
|
|
909
1006
|
end
|
|
@@ -927,22 +1024,20 @@ module RubyLLM
|
|
|
927
1024
|
input_values = input_values.merge(inputs || {})
|
|
928
1025
|
|
|
929
1026
|
if chat
|
|
930
|
-
|
|
931
|
-
self.class.apply_configuration(@chat, input_values:, persist_instructions:)
|
|
1027
|
+
bind(chat, input_values)
|
|
1028
|
+
self.class.apply_configuration(@chat, input_values:, persist_instructions:, agent: self)
|
|
932
1029
|
return
|
|
933
1030
|
end
|
|
934
1031
|
|
|
935
1032
|
context_value = self.class.resolved_context(inputs: input_values)
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
options: chat_options,
|
|
939
|
-
resolved_context: context_value
|
|
940
|
-
)
|
|
1033
|
+
bind(self.class.build_chat(inputs: input_values, options: chat_options, resolved_context: context_value),
|
|
1034
|
+
input_values)
|
|
941
1035
|
self.class.apply_configuration(
|
|
942
1036
|
@chat,
|
|
943
1037
|
input_values:,
|
|
944
1038
|
persist_instructions:,
|
|
945
|
-
resolved_context: context_value
|
|
1039
|
+
resolved_context: context_value,
|
|
1040
|
+
agent: self
|
|
946
1041
|
)
|
|
947
1042
|
end
|
|
948
1043
|
|
|
@@ -953,6 +1048,207 @@ module RubyLLM
|
|
|
953
1048
|
# #chat without adapting its return values.
|
|
954
1049
|
def_delegators :chat, *CHAINABLE_CHAT_DELEGATES, *PASSTHROUGH_CHAT_DELEGATES
|
|
955
1050
|
|
|
1051
|
+
##
|
|
1052
|
+
# :singleton-method: rescue_from
|
|
1053
|
+
# :call-seq: rescue_from(*exception_classes, with: nil, &block)
|
|
1054
|
+
#
|
|
1055
|
+
# Registers a handler for exceptions raised by the chat operations of
|
|
1056
|
+
# this agent's instances and of the chats and records ::chat, ::create,
|
|
1057
|
+
# ::create!, and ::find return: #ask, #say, #ask_later, #complete,
|
|
1058
|
+
# #generate, #run_tools, #step, #count_tokens, and #compact. Name the
|
|
1059
|
+
# handler with +with:+ or pass a block; either runs on the agent instance,
|
|
1060
|
+
# so the agent's #chat, inputs, and class name are available for instrumentation.
|
|
1061
|
+
#
|
|
1062
|
+
# class ApplicationAgent < RubyLLM::Agent
|
|
1063
|
+
# rescue_from RubyLLM::RateLimitError, RubyLLM::ConnectionError, with: :handle_transient
|
|
1064
|
+
# rescue_from RubyLLM::BadRequestError do |error|
|
|
1065
|
+
# error_tracker.notify(error)
|
|
1066
|
+
# raise
|
|
1067
|
+
# end
|
|
1068
|
+
#
|
|
1069
|
+
# private
|
|
1070
|
+
#
|
|
1071
|
+
# def handle_transient(error)
|
|
1072
|
+
# metrics.increment("llm.api_error", type: "transient")
|
|
1073
|
+
# raise
|
|
1074
|
+
# end
|
|
1075
|
+
# end
|
|
1076
|
+
#
|
|
1077
|
+
# Handlers are searched in reverse declaration order, so the last
|
|
1078
|
+
# matching one wins. Re-raise inside a handler to let the caller see
|
|
1079
|
+
# the exception; otherwise the handler's return value becomes the
|
|
1080
|
+
# operation's return value. Exceptions no handler matches are
|
|
1081
|
+
# re-raised. Subclasses use their parents' handlers, including ones
|
|
1082
|
+
# declared after the subclass, and their own come first.
|
|
1083
|
+
#
|
|
1084
|
+
# Exception classes may be named as Strings, which defers constant
|
|
1085
|
+
# lookup until an exception is raised. A class that can never match a
|
|
1086
|
+
# StandardError, such as Interrupt, raises ArgumentError.
|
|
1087
|
+
|
|
1088
|
+
##
|
|
1089
|
+
# :method: rescue_with_handler
|
|
1090
|
+
# :call-seq: rescue_with_handler(exception)
|
|
1091
|
+
#
|
|
1092
|
+
# Runs the ::rescue_from handler matching +exception+ and returns its
|
|
1093
|
+
# value, or re-raises when no handler matches. The chat operations call
|
|
1094
|
+
# this for you.
|
|
1095
|
+
|
|
1096
|
+
##
|
|
1097
|
+
# :singleton-method: description
|
|
1098
|
+
# :call-seq: description(text = nil)
|
|
1099
|
+
#
|
|
1100
|
+
# Sets what the agent does, which a model reads when the agent is one
|
|
1101
|
+
# of its tools (see ::to_tool), or returns it when called without an
|
|
1102
|
+
# argument.
|
|
1103
|
+
#
|
|
1104
|
+
# class ResearchAgent < RubyLLM::Agent
|
|
1105
|
+
# description "Researches a question across the web and reports back"
|
|
1106
|
+
# end
|
|
1107
|
+
|
|
1108
|
+
##
|
|
1109
|
+
# :singleton-method: to_tool
|
|
1110
|
+
# :call-seq: to_tool(**inputs)
|
|
1111
|
+
#
|
|
1112
|
+
# Returns an AgentTool that runs this agent as a subagent: a model
|
|
1113
|
+
# hands it a task and gets its answer back. Keywords matching ::inputs
|
|
1114
|
+
# are given to every run; the agent's other inputs become parameters
|
|
1115
|
+
# the model fills. Chat#with_tools and ::tools take an Agent class too,
|
|
1116
|
+
# and convert it with no inputs.
|
|
1117
|
+
#
|
|
1118
|
+
# chat.with_tools(ResearchAgent.to_tool(workspace: current_workspace))
|
|
1119
|
+
# chat.with_tools(ResearchAgent)
|
|
1120
|
+
#
|
|
1121
|
+
# Raises ArgumentError for an agent class without a name.
|
|
1122
|
+
|
|
1123
|
+
##
|
|
1124
|
+
# :singleton-method: after_message
|
|
1125
|
+
# :call-seq: after_message(method_name = nil, if: nil, unless: nil, &block)
|
|
1126
|
+
#
|
|
1127
|
+
# Registers a Chat#after_message callback on every chat the agent builds
|
|
1128
|
+
# or finds, Agent::find records included. Name a method or pass a block;
|
|
1129
|
+
# either runs on the agent instance, so its inputs, +chat+, and private
|
|
1130
|
+
# methods are available, and receives the callback's arguments when it
|
|
1131
|
+
# takes them. +if:+ and +unless:+ take a method name, a lambda, or an
|
|
1132
|
+
# Array of them, like Rails callbacks. Subclasses inherit the callbacks
|
|
1133
|
+
# declared when they are defined. Returns the agent class.
|
|
1134
|
+
#
|
|
1135
|
+
# For a chat from ::chat or ::find, the agent is bound to the chat
|
|
1136
|
+
# without running +initialize+, as for ::rescue_from handlers, so read
|
|
1137
|
+
# inputs and +chat+ rather than instance variables +initialize+ sets.
|
|
1138
|
+
# Wrapping a configured chat in a new agent, as
|
|
1139
|
+
# <tt>SupportAgent.new(chat:)</tt> does, moves its callbacks to that
|
|
1140
|
+
# agent instead of registering them twice.
|
|
1141
|
+
#
|
|
1142
|
+
# class SupportAgent < RubyLLM::Agent
|
|
1143
|
+
# inputs :ticket
|
|
1144
|
+
#
|
|
1145
|
+
# after_message :log_reply, if: -> { ticket.urgent? }
|
|
1146
|
+
# before_tool_call { |tool_call| Rails.logger.info "Calling #{tool_call.name}" }
|
|
1147
|
+
#
|
|
1148
|
+
# private
|
|
1149
|
+
#
|
|
1150
|
+
# def log_reply(message) = ticket.notes.create!(body: message.content)
|
|
1151
|
+
# end
|
|
1152
|
+
#
|
|
1153
|
+
# The other chat callbacks have macros of the same name and shape:
|
|
1154
|
+
# ::before_request, ::before_message, ::before_tool_call,
|
|
1155
|
+
# ::around_tool_call, ::after_tool_result, ::after_tool_progress, ::before_fallback,
|
|
1156
|
+
# ::after_fallback, and ::after_file_write. Raises ArgumentError for
|
|
1157
|
+
# both a method name and a block, neither, or another option.
|
|
1158
|
+
|
|
1159
|
+
##
|
|
1160
|
+
# :singleton-method: before_request
|
|
1161
|
+
# :call-seq: before_request(method_name = nil, if: nil, unless: nil, &block)
|
|
1162
|
+
#
|
|
1163
|
+
# Registers a Chat#before_request callback on every chat the agent builds or
|
|
1164
|
+
# finds. See ::after_message.
|
|
1165
|
+
|
|
1166
|
+
##
|
|
1167
|
+
# :singleton-method: before_message
|
|
1168
|
+
# :call-seq: before_message(method_name = nil, if: nil, unless: nil, &block)
|
|
1169
|
+
#
|
|
1170
|
+
# Registers a Chat#before_message callback on every chat the agent builds or
|
|
1171
|
+
# finds. See ::after_message.
|
|
1172
|
+
|
|
1173
|
+
##
|
|
1174
|
+
# :singleton-method: before_tool_call
|
|
1175
|
+
# :call-seq: before_tool_call(method_name = nil, if: nil, unless: nil, &block)
|
|
1176
|
+
#
|
|
1177
|
+
# Registers a Chat#before_tool_call callback on every chat the agent builds or
|
|
1178
|
+
# finds. See ::after_message.
|
|
1179
|
+
|
|
1180
|
+
##
|
|
1181
|
+
# :singleton-method: around_tool_call
|
|
1182
|
+
# :call-seq: around_tool_call(method_name = nil, if: nil, unless: nil, &block)
|
|
1183
|
+
#
|
|
1184
|
+
# Registers a Chat#around_tool_call callback on every chat the agent builds
|
|
1185
|
+
# or finds. The method or block receives the ToolCall and a block that
|
|
1186
|
+
# runs the tool. A method yields to it; a block takes it as +&run+:
|
|
1187
|
+
#
|
|
1188
|
+
# class SupportAgent < RubyLLM::Agent
|
|
1189
|
+
# inputs :account
|
|
1190
|
+
#
|
|
1191
|
+
# around_tool_call :in_tenant
|
|
1192
|
+
#
|
|
1193
|
+
# private
|
|
1194
|
+
#
|
|
1195
|
+
# def in_tenant(tool_call, &) = ActsAsTenant.with_tenant(account, &)
|
|
1196
|
+
# end
|
|
1197
|
+
#
|
|
1198
|
+
# When +if:+ or +unless:+ skips the callback, the tool runs without it.
|
|
1199
|
+
# See ::after_message.
|
|
1200
|
+
|
|
1201
|
+
##
|
|
1202
|
+
# :singleton-method: after_tool_result
|
|
1203
|
+
# :call-seq: after_tool_result(method_name = nil, if: nil, unless: nil, &block)
|
|
1204
|
+
#
|
|
1205
|
+
# Registers a Chat#after_tool_result callback on every chat the agent builds or
|
|
1206
|
+
# finds. See ::after_message.
|
|
1207
|
+
|
|
1208
|
+
##
|
|
1209
|
+
# :singleton-method: after_tool_progress
|
|
1210
|
+
# :call-seq: after_tool_progress(method_name = nil, if: nil, unless: nil, &block)
|
|
1211
|
+
#
|
|
1212
|
+
# Registers a Chat#after_tool_progress callback on every chat the agent builds or
|
|
1213
|
+
# finds. See ::after_message.
|
|
1214
|
+
|
|
1215
|
+
##
|
|
1216
|
+
# :singleton-method: before_fallback
|
|
1217
|
+
# :call-seq: before_fallback(method_name = nil, if: nil, unless: nil, &block)
|
|
1218
|
+
#
|
|
1219
|
+
# Registers a Chat#before_fallback callback on every chat the agent builds or
|
|
1220
|
+
# finds. See ::after_message.
|
|
1221
|
+
|
|
1222
|
+
##
|
|
1223
|
+
# :singleton-method: after_fallback
|
|
1224
|
+
# :call-seq: after_fallback(method_name = nil, if: nil, unless: nil, &block)
|
|
1225
|
+
#
|
|
1226
|
+
# Registers a Chat#after_fallback callback on every chat the agent builds or
|
|
1227
|
+
# finds. See ::after_message.
|
|
1228
|
+
|
|
1229
|
+
##
|
|
1230
|
+
# :singleton-method: after_file_write
|
|
1231
|
+
# :call-seq: after_file_write(method_name = nil, if: nil, unless: nil, &block)
|
|
1232
|
+
#
|
|
1233
|
+
# Registers a Chat#after_file_write callback on every chat the agent builds or
|
|
1234
|
+
# finds. See ::after_message.
|
|
1235
|
+
|
|
1236
|
+
##
|
|
1237
|
+
# :singleton-method: around_join
|
|
1238
|
+
# :call-seq: around_join(&block)
|
|
1239
|
+
#
|
|
1240
|
+
# Registers +block+ as a Chat#around_join callback on every chat the
|
|
1241
|
+
# agent builds or finds. Subclasses inherit the callbacks declared
|
|
1242
|
+
# when they are defined. Returns the list of callbacks.
|
|
1243
|
+
#
|
|
1244
|
+
# class SupportAgent < RubyLLM::Agent
|
|
1245
|
+
# around_join do |queued, join|
|
|
1246
|
+
# join.call(sender: queued.sender)
|
|
1247
|
+
# end
|
|
1248
|
+
# end
|
|
1249
|
+
#
|
|
1250
|
+
# Raises ArgumentError without a block.
|
|
1251
|
+
|
|
956
1252
|
##
|
|
957
1253
|
# :method: with_instructions
|
|
958
1254
|
# :call-seq: with_instructions(instructions, append: false, cache_until_here: false)
|
|
@@ -1019,6 +1315,18 @@ module RubyLLM
|
|
|
1019
1315
|
#
|
|
1020
1316
|
# Delegates to Chat#with_end_user. See that method for arguments and return values.
|
|
1021
1317
|
|
|
1318
|
+
##
|
|
1319
|
+
# :method: with_owner
|
|
1320
|
+
# :call-seq: with_owner(owner)
|
|
1321
|
+
#
|
|
1322
|
+
# Delegates to Chat#with_owner. See that method for arguments and return values.
|
|
1323
|
+
|
|
1324
|
+
##
|
|
1325
|
+
# :method: with_context_budget
|
|
1326
|
+
# :call-seq: with_context_budget(budget = {}, per_result: nil, per_request: nil, window: nil)
|
|
1327
|
+
#
|
|
1328
|
+
# Delegates to Chat#with_context_budget. See that method for arguments and return values.
|
|
1329
|
+
|
|
1022
1330
|
##
|
|
1023
1331
|
# :method: with_compaction
|
|
1024
1332
|
# :call-seq: with_compaction(options = {})
|
|
@@ -1031,6 +1339,24 @@ module RubyLLM
|
|
|
1031
1339
|
#
|
|
1032
1340
|
# Delegates to Chat#with_caching. See that method for arguments and return values.
|
|
1033
1341
|
|
|
1342
|
+
##
|
|
1343
|
+
# :method: with_code_execution
|
|
1344
|
+
# :call-seq: with_code_execution(enabled = true, **options)
|
|
1345
|
+
#
|
|
1346
|
+
# Delegates to Chat#with_code_execution. See that method for arguments and return values.
|
|
1347
|
+
|
|
1348
|
+
##
|
|
1349
|
+
# :method: with_skills
|
|
1350
|
+
# :call-seq: with_skills(*skills)
|
|
1351
|
+
#
|
|
1352
|
+
# Delegates to Chat#with_skills. See that method for arguments and return values.
|
|
1353
|
+
|
|
1354
|
+
##
|
|
1355
|
+
# :method: with_file_store
|
|
1356
|
+
# :call-seq: with_file_store(store = :memory, inline_limit: 10_000, tools: %i[read write edit])
|
|
1357
|
+
#
|
|
1358
|
+
# Delegates to Chat#with_file_store. See that method for arguments and return values.
|
|
1359
|
+
|
|
1034
1360
|
##
|
|
1035
1361
|
# :method: with_context
|
|
1036
1362
|
# :call-seq: with_context(context)
|
|
@@ -1057,7 +1383,7 @@ module RubyLLM
|
|
|
1057
1383
|
|
|
1058
1384
|
##
|
|
1059
1385
|
# :method: with_fallbacks
|
|
1060
|
-
# :call-seq: with_fallbacks(*models, on: Fallback::DEFAULT_ERRORS)
|
|
1386
|
+
# :call-seq: with_fallbacks(*models, on: Fallback::DEFAULT_ERRORS, drop_unsupported: false)
|
|
1061
1387
|
#
|
|
1062
1388
|
# Delegates to Chat#with_fallbacks. See that method for arguments and return values.
|
|
1063
1389
|
|
|
@@ -1097,6 +1423,12 @@ module RubyLLM
|
|
|
1097
1423
|
#
|
|
1098
1424
|
# Delegates to Chat#after_tool_result. See that method for arguments and return values.
|
|
1099
1425
|
|
|
1426
|
+
##
|
|
1427
|
+
# :method: around_tool_call
|
|
1428
|
+
# :call-seq: around_tool_call(&block)
|
|
1429
|
+
#
|
|
1430
|
+
# Delegates to Chat#around_tool_call. See that method for arguments and return values.
|
|
1431
|
+
|
|
1100
1432
|
##
|
|
1101
1433
|
# :method: after_tool_progress
|
|
1102
1434
|
# :call-seq: after_tool_progress(&block)
|
|
@@ -1115,6 +1447,24 @@ module RubyLLM
|
|
|
1115
1447
|
#
|
|
1116
1448
|
# Delegates to Chat#after_fallback. See that method for arguments and return values.
|
|
1117
1449
|
|
|
1450
|
+
##
|
|
1451
|
+
# :method: around_join
|
|
1452
|
+
# :call-seq: around_join(&block)
|
|
1453
|
+
#
|
|
1454
|
+
# Delegates to Chat#around_join. See that method for arguments and return values.
|
|
1455
|
+
|
|
1456
|
+
##
|
|
1457
|
+
# :method: join_queued_messages
|
|
1458
|
+
# :call-seq: join_queued_messages
|
|
1459
|
+
#
|
|
1460
|
+
# Delegates to Chat#join_queued_messages. See that method for arguments and return values.
|
|
1461
|
+
|
|
1462
|
+
##
|
|
1463
|
+
# :method: after_file_write
|
|
1464
|
+
# :call-seq: after_file_write(&block)
|
|
1465
|
+
#
|
|
1466
|
+
# Delegates to Chat#after_file_write. See that method for arguments and return values.
|
|
1467
|
+
|
|
1118
1468
|
##
|
|
1119
1469
|
# :method: cancel
|
|
1120
1470
|
# :call-seq: cancel()
|
|
@@ -1123,25 +1473,19 @@ module RubyLLM
|
|
|
1123
1473
|
|
|
1124
1474
|
##
|
|
1125
1475
|
# :method: approve
|
|
1126
|
-
# :call-seq: approve(tool_call)
|
|
1476
|
+
# :call-seq: approve(tool_call, remember: false)
|
|
1127
1477
|
#
|
|
1128
1478
|
# Delegates to Chat#approve. See that method for arguments and return values.
|
|
1129
1479
|
|
|
1130
|
-
##
|
|
1131
|
-
# :method: deny
|
|
1132
|
-
# :call-seq: deny(tool_call)
|
|
1133
|
-
#
|
|
1134
|
-
# Delegates to Chat#deny. See that method for arguments and return values.
|
|
1135
|
-
|
|
1136
1480
|
##
|
|
1137
1481
|
# :method: answer
|
|
1138
|
-
# :call-seq: answer(
|
|
1482
|
+
# :call-seq: answer(question, value = nil, **values)
|
|
1139
1483
|
#
|
|
1140
1484
|
# Delegates to Chat#answer. See that method for arguments and return values.
|
|
1141
1485
|
|
|
1142
1486
|
##
|
|
1143
1487
|
# :method: decline
|
|
1144
|
-
# :call-seq: decline(
|
|
1488
|
+
# :call-seq: decline(question, note = nil)
|
|
1145
1489
|
#
|
|
1146
1490
|
# Delegates to Chat#decline. See that method for arguments and return values.
|
|
1147
1491
|
|
|
@@ -1149,7 +1493,9 @@ module RubyLLM
|
|
|
1149
1493
|
# :method: cache_until_here
|
|
1150
1494
|
# :call-seq: cache_until_here(ttl: nil)
|
|
1151
1495
|
#
|
|
1152
|
-
#
|
|
1496
|
+
# Deprecated: use Message#cache_until_here or
|
|
1497
|
+
# <tt>with_instructions(cache_until_here:)</tt>. Delegates to
|
|
1498
|
+
# Chat#cache_until_here, which warns. RubyLLM 3.0 removes it.
|
|
1153
1499
|
|
|
1154
1500
|
##
|
|
1155
1501
|
# :method: model
|
|
@@ -1235,12 +1581,36 @@ module RubyLLM
|
|
|
1235
1581
|
#
|
|
1236
1582
|
# Delegates to Chat#citations. See that method for arguments and return values.
|
|
1237
1583
|
|
|
1584
|
+
##
|
|
1585
|
+
# :method: code_execution
|
|
1586
|
+
# :call-seq: code_execution
|
|
1587
|
+
#
|
|
1588
|
+
# Delegates to Chat#code_execution. See that method for arguments and return values.
|
|
1589
|
+
|
|
1590
|
+
##
|
|
1591
|
+
# :method: skills
|
|
1592
|
+
# :call-seq: skills
|
|
1593
|
+
#
|
|
1594
|
+
# Delegates to Chat#skills. See that method for arguments and return values.
|
|
1595
|
+
|
|
1596
|
+
##
|
|
1597
|
+
# :method: file_store
|
|
1598
|
+
# :call-seq: file_store
|
|
1599
|
+
#
|
|
1600
|
+
# Delegates to Chat#file_store. See that method for arguments and return values.
|
|
1601
|
+
|
|
1238
1602
|
##
|
|
1239
1603
|
# :method: compaction
|
|
1240
1604
|
# :call-seq: compaction
|
|
1241
1605
|
#
|
|
1242
1606
|
# Delegates to Chat#compaction. See that method for arguments and return values.
|
|
1243
1607
|
|
|
1608
|
+
##
|
|
1609
|
+
# :method: context_budget
|
|
1610
|
+
# :call-seq: context_budget
|
|
1611
|
+
#
|
|
1612
|
+
# Delegates to Chat#context_budget. See that method for arguments and return values.
|
|
1613
|
+
|
|
1244
1614
|
##
|
|
1245
1615
|
# :method: context
|
|
1246
1616
|
# :call-seq: context
|
|
@@ -1281,7 +1651,15 @@ module RubyLLM
|
|
|
1281
1651
|
# :method: each
|
|
1282
1652
|
# :call-seq: each(&block)
|
|
1283
1653
|
#
|
|
1284
|
-
# Delegates to Chat#each
|
|
1654
|
+
# Deprecated: iterate #messages instead. Delegates to Chat#each, which
|
|
1655
|
+
# warns, as do the Enumerable methods built on it. RubyLLM 3.0 removes
|
|
1656
|
+
# them.
|
|
1657
|
+
|
|
1658
|
+
##
|
|
1659
|
+
# :method: model_id
|
|
1660
|
+
# :call-seq: model_id()
|
|
1661
|
+
#
|
|
1662
|
+
# Delegates to Chat#model_id. See that method for arguments and return values.
|
|
1285
1663
|
|
|
1286
1664
|
##
|
|
1287
1665
|
# :method: complete?
|
|
@@ -1302,46 +1680,40 @@ module RubyLLM
|
|
|
1302
1680
|
# Delegates to Chat#waiting?. See that method for arguments and return values.
|
|
1303
1681
|
|
|
1304
1682
|
##
|
|
1305
|
-
# :method:
|
|
1306
|
-
# :call-seq:
|
|
1683
|
+
# :method: pending_questions
|
|
1684
|
+
# :call-seq: pending_questions()
|
|
1307
1685
|
#
|
|
1308
|
-
# Delegates to Chat#
|
|
1686
|
+
# Delegates to Chat#pending_questions. See that method for arguments and return values.
|
|
1309
1687
|
|
|
1310
1688
|
##
|
|
1311
|
-
# :method:
|
|
1312
|
-
# :call-seq:
|
|
1313
|
-
#
|
|
1314
|
-
# Delegates to Chat#pending_approvals. See that method for arguments and return values.
|
|
1315
|
-
|
|
1316
|
-
##
|
|
1317
|
-
# :method: awaiting_input?
|
|
1318
|
-
# :call-seq: awaiting_input?()
|
|
1689
|
+
# :method: pending_tasks
|
|
1690
|
+
# :call-seq: pending_tasks()
|
|
1319
1691
|
#
|
|
1320
|
-
# Delegates to Chat#
|
|
1692
|
+
# Delegates to Chat#pending_tasks. See that method for arguments and return values.
|
|
1321
1693
|
|
|
1322
1694
|
##
|
|
1323
|
-
# :method:
|
|
1324
|
-
# :call-seq:
|
|
1695
|
+
# :method: add_message
|
|
1696
|
+
# :call-seq: add_message(message_or_attributes)
|
|
1325
1697
|
#
|
|
1326
|
-
# Delegates to Chat#
|
|
1698
|
+
# Delegates to Chat#add_message. See that method for arguments and return values.
|
|
1327
1699
|
|
|
1328
1700
|
##
|
|
1329
|
-
# :method:
|
|
1330
|
-
# :call-seq:
|
|
1701
|
+
# :method: queue
|
|
1702
|
+
# :call-seq: queue(message = nil, with: nil, steer: true, sender: nil)
|
|
1331
1703
|
#
|
|
1332
|
-
# Delegates to Chat#
|
|
1704
|
+
# Delegates to Chat#queue. See that method for arguments and return values.
|
|
1333
1705
|
|
|
1334
1706
|
##
|
|
1335
|
-
# :method:
|
|
1336
|
-
# :call-seq:
|
|
1707
|
+
# :method: queued_messages
|
|
1708
|
+
# :call-seq: queued_messages()
|
|
1337
1709
|
#
|
|
1338
|
-
# Delegates to Chat#
|
|
1710
|
+
# Delegates to Chat#queued_messages. See that method for arguments and return values.
|
|
1339
1711
|
|
|
1340
1712
|
##
|
|
1341
|
-
# :method:
|
|
1342
|
-
# :call-seq:
|
|
1713
|
+
# :method: withdraw
|
|
1714
|
+
# :call-seq: withdraw(queued)
|
|
1343
1715
|
#
|
|
1344
|
-
# Delegates to Chat#
|
|
1716
|
+
# Delegates to Chat#withdraw. See that method for arguments and return values.
|
|
1345
1717
|
|
|
1346
1718
|
##
|
|
1347
1719
|
# :method: tokens
|
|
@@ -1423,25 +1795,37 @@ module RubyLLM
|
|
|
1423
1795
|
# Compacts the model context through Chat#compact and returns its Message.
|
|
1424
1796
|
# Exceptions use ::rescue_from handlers.
|
|
1425
1797
|
|
|
1426
|
-
|
|
1798
|
+
Chat::Rescuable::OPERATIONS.each do |operation|
|
|
1427
1799
|
define_method(operation) do |*args, **kwargs, &block|
|
|
1428
|
-
chat.public_send(operation, *args, **kwargs, &block)
|
|
1429
|
-
rescue StandardError => e
|
|
1430
|
-
rescue_with_handler(e)
|
|
1800
|
+
rescuing { chat.public_send(operation, *args, **kwargs, &block) }
|
|
1431
1801
|
end
|
|
1432
1802
|
end
|
|
1433
1803
|
|
|
1434
|
-
#
|
|
1435
|
-
#
|
|
1436
|
-
#
|
|
1437
|
-
def
|
|
1438
|
-
|
|
1439
|
-
|
|
1804
|
+
# Returns whoever the usage of the agent's chat is attributed to, as
|
|
1805
|
+
# Chat#owner describes. For a persisted chat, reads it from the
|
|
1806
|
+
# record's chat, since the record leaves +owner+ to the application.
|
|
1807
|
+
def owner
|
|
1808
|
+
(chat.respond_to?(:to_llm) ? chat.to_llm : chat).owner
|
|
1809
|
+
end
|
|
1440
1810
|
|
|
1441
|
-
|
|
1811
|
+
def bind(chat, input_values) # :nodoc:
|
|
1812
|
+
@chat = chat
|
|
1813
|
+
input_values.each do |name, value|
|
|
1814
|
+
define_singleton_method(name) { value } unless respond_to?(name)
|
|
1815
|
+
end
|
|
1816
|
+
self
|
|
1817
|
+
end
|
|
1818
|
+
|
|
1819
|
+
private
|
|
1820
|
+
|
|
1821
|
+
def rescuing(&)
|
|
1822
|
+
return chat.rescuing_with(self, &) if chat.is_a?(Chat::Rescuable)
|
|
1442
1823
|
|
|
1443
|
-
|
|
1444
|
-
|
|
1824
|
+
begin
|
|
1825
|
+
yield
|
|
1826
|
+
rescue StandardError => e
|
|
1827
|
+
rescue_with_handler(e)
|
|
1828
|
+
end
|
|
1445
1829
|
end
|
|
1446
1830
|
end
|
|
1447
1831
|
end
|