ruby_llm 1.15.0 → 2.0.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rdoc_options +25 -0
- data/README.md +87 -33
- data/exe/ruby_llm +8 -0
- data/lib/generators/ruby_llm/agent/templates/agent.rb.tt +0 -1
- data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +3 -43
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt +11 -3
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt +8 -0
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt +4 -4
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_chat.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_form.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/index.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/show.html.erb.tt +2 -2
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_assistant.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_system.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_tool.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_tool_calls.html.erb.tt +6 -4
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_user.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/tool_calls/_default.html.erb.tt +2 -2
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/_model.html.erb.tt +5 -6
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/index.html.erb.tt +2 -2
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/show.html.erb.tt +5 -5
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt +2 -2
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_assistant.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_system.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool_calls.html.erb.tt +6 -4
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_user.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt +4 -6
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/tool_calls/_default.html.erb.tt +2 -2
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt +5 -6
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt +2 -2
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt +3 -3
- data/lib/generators/ruby_llm/generator_helpers.rb +106 -62
- data/lib/generators/ruby_llm/install/install_generator.rb +3 -11
- data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +2 -0
- data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +14 -6
- data/lib/generators/ruby_llm/install/templates/create_ruby_llm_records_migration.rb.tt +117 -0
- data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +0 -8
- data/lib/generators/ruby_llm/provider/cli.rb +175 -0
- data/lib/generators/ruby_llm/provider/scaffold.rb +323 -0
- data/lib/generators/ruby_llm/provider/templates/core/provider.rb.erb +37 -0
- data/lib/generators/ruby_llm/provider/templates/core/provider_spec.rb.erb +34 -0
- data/lib/generators/ruby_llm/provider/templates/gem/archspec.rb.erb +14 -0
- data/lib/generators/ruby_llm/provider/templates/gem/bin/console.erb +15 -0
- data/lib/generators/ruby_llm/provider/templates/gem/bin/setup.erb +5 -0
- data/lib/generators/ruby_llm/provider/templates/gem/chat_schema_spec.rb.erb +30 -0
- data/lib/generators/ruby_llm/provider/templates/gem/chat_spec.rb.erb +35 -0
- data/lib/generators/ruby_llm/provider/templates/gem/chat_streaming_spec.rb.erb +22 -0
- data/lib/generators/ruby_llm/provider/templates/gem/chat_tools_spec.rb.erb +30 -0
- data/lib/generators/ruby_llm/provider/templates/gem/ci.yml.erb +32 -0
- data/lib/generators/ruby_llm/provider/templates/gem/embedding_spec.rb.erb +49 -0
- data/lib/generators/ruby_llm/provider/templates/gem/env.erb +2 -0
- data/lib/generators/ruby_llm/provider/templates/gem/fixtures_gitkeep.erb +1 -0
- data/lib/generators/ruby_llm/provider/templates/gem/flayignore.erb +1 -0
- data/lib/generators/ruby_llm/provider/templates/gem/gemfile.erb +25 -0
- data/lib/generators/ruby_llm/provider/templates/gem/gemspec.erb +28 -0
- data/lib/generators/ruby_llm/provider/templates/gem/gitignore.erb +7 -0
- data/lib/generators/ruby_llm/provider/templates/gem/gitleaks.yml.erb +22 -0
- data/lib/generators/ruby_llm/provider/templates/gem/image_spec.rb.erb +23 -0
- data/lib/generators/ruby_llm/provider/templates/gem/license.erb +21 -0
- data/lib/generators/ruby_llm/provider/templates/gem/models.rb.erb +19 -0
- data/lib/generators/ruby_llm/provider/templates/gem/models_spec.rb.erb +17 -0
- data/lib/generators/ruby_llm/provider/templates/gem/moderation_spec.rb.erb +22 -0
- data/lib/generators/ruby_llm/provider/templates/gem/overcommit.yml.erb +31 -0
- data/lib/generators/ruby_llm/provider/templates/gem/provider.rb.erb +52 -0
- data/lib/generators/ruby_llm/provider/templates/gem/provider_spec.rb.erb +38 -0
- data/lib/generators/ruby_llm/provider/templates/gem/rakefile.erb +38 -0
- data/lib/generators/ruby_llm/provider/templates/gem/readme.md.erb +50 -0
- data/lib/generators/ruby_llm/provider/templates/gem/release.yml.erb +36 -0
- data/lib/generators/ruby_llm/provider/templates/gem/rerank_spec.rb.erb +23 -0
- data/lib/generators/ruby_llm/provider/templates/gem/rspec.erb +2 -0
- data/lib/generators/ruby_llm/provider/templates/gem/rubocop.yml.erb +29 -0
- data/lib/generators/ruby_llm/provider/templates/gem/rubyllm_configuration.rb.erb +14 -0
- data/lib/generators/ruby_llm/provider/templates/gem/spec_helper.rb.erb +26 -0
- data/lib/generators/ruby_llm/provider/templates/gem/speech_spec.rb.erb +25 -0
- data/lib/generators/ruby_llm/provider/templates/gem/vcr_configuration.rb.erb +16 -0
- data/lib/generators/ruby_llm/provider/templates/gem/video_spec.rb.erb +27 -0
- data/lib/generators/ruby_llm/schema/schema_generator.rb +5 -1
- data/lib/generators/ruby_llm/schema/templates/schema.rb.tt +1 -1
- data/lib/generators/ruby_llm/tool/templates/tailwind/tool_call.html.erb.tt +13 -0
- data/lib/generators/ruby_llm/tool/templates/tailwind/tool_result.html.erb.tt +21 -0
- data/lib/generators/ruby_llm/tool/templates/tool.rb.tt +3 -3
- data/lib/generators/ruby_llm/tool/templates/tool_call.html.erb.tt +7 -12
- data/lib/generators/ruby_llm/tool/templates/tool_result.html.erb.tt +5 -2
- data/lib/generators/ruby_llm/tool/tool_generator.rb +25 -59
- data/lib/generators/ruby_llm/upgrade/templates/backfill_v2_data.rb.tt +461 -0
- data/lib/generators/ruby_llm/upgrade/templates/cleanup_v2_upgrade.rb.tt +100 -0
- data/lib/generators/ruby_llm/upgrade/templates/finish_v2_upgrade.rb.tt +215 -0
- data/lib/generators/ruby_llm/upgrade/templates/prepare_v2_upgrade.rb.tt +660 -0
- data/lib/generators/ruby_llm/upgrade/templates/ruby_llm_upgrade.rb.tt +222 -0
- data/lib/generators/ruby_llm/upgrade/templates/upgrade_initializer.rb.tt +13 -0
- data/lib/generators/ruby_llm/upgrade/upgrade_generator.rb +167 -0
- data/lib/generators/ruby_llm/upgrade/upgrade_migration.rb +344 -0
- data/lib/ruby_llm/accounting/usage.rb +245 -0
- data/lib/ruby_llm/active_record/acts_as.rb +94 -136
- data/lib/ruby_llm/active_record/attachment_helpers.rb +180 -0
- data/lib/ruby_llm/active_record/batch.rb +97 -0
- data/lib/ruby_llm/active_record/chat_methods.rb +823 -305
- data/lib/ruby_llm/active_record/message_methods.rb +119 -75
- data/lib/ruby_llm/active_record/model.rb +135 -0
- data/lib/ruby_llm/active_record/payload_helpers.rb +1 -2
- data/lib/ruby_llm/active_record/tool_call.rb +33 -0
- data/lib/ruby_llm/active_record/usage.rb +61 -0
- data/lib/ruby_llm/agent.rb +1065 -150
- data/lib/ruby_llm/aliases.json +338 -167
- data/lib/ruby_llm/attachment.rb +217 -61
- data/lib/ruby_llm/batch.rb +432 -0
- data/lib/ruby_llm/cached_content.rb +112 -0
- data/lib/ruby_llm/chat/tool_concurrency.rb +111 -0
- data/lib/ruby_llm/chat.rb +1208 -150
- data/lib/ruby_llm/chunk.rb +10 -0
- data/lib/ruby_llm/citation.rb +105 -0
- data/lib/ruby_llm/configuration.rb +274 -24
- data/lib/ruby_llm/context.rb +128 -6
- data/lib/ruby_llm/cost.rb +217 -80
- data/lib/ruby_llm/downloaded_file.rb +33 -0
- data/lib/ruby_llm/embedding.rb +141 -7
- data/lib/ruby_llm/embedding_request.rb +53 -0
- data/lib/ruby_llm/error.rb +161 -89
- data/lib/ruby_llm/fallback.rb +133 -0
- data/lib/ruby_llm/files/mime_type.rb +97 -0
- data/lib/ruby_llm/image.rb +155 -32
- data/lib/ruby_llm/message.rb +233 -54
- data/lib/ruby_llm/model/modalities.rb +17 -4
- data/lib/ruby_llm/model/pricing.rb +43 -14
- data/lib/ruby_llm/model/pricing_category.rb +103 -14
- data/lib/ruby_llm/model/pricing_tier.rb +66 -15
- data/lib/ruby_llm/model.rb +244 -2
- data/lib/ruby_llm/models/aliases.rb +41 -0
- data/lib/ruby_llm/models/registry.rb +165 -0
- data/lib/ruby_llm/models/schema.rb +99 -0
- data/lib/ruby_llm/models.json +70380 -33380
- data/lib/ruby_llm/models.rb +528 -201
- data/lib/ruby_llm/moderation.rb +139 -26
- data/lib/ruby_llm/ocr.rb +112 -0
- data/lib/ruby_llm/prompt.rb +79 -0
- data/lib/ruby_llm/protocol/binary_streaming.rb +65 -0
- data/lib/ruby_llm/protocol/stream_accumulator.rb +214 -0
- data/lib/ruby_llm/protocol/streaming.rb +230 -0
- data/lib/ruby_llm/protocol.rb +662 -0
- data/lib/ruby_llm/protocols/anthropic/batches.rb +73 -0
- data/lib/ruby_llm/protocols/anthropic/chat.rb +540 -0
- data/lib/ruby_llm/protocols/anthropic/embeddings.rb +14 -0
- data/lib/ruby_llm/protocols/anthropic/files.rb +38 -0
- data/lib/ruby_llm/protocols/anthropic/media.rb +141 -0
- data/lib/ruby_llm/protocols/anthropic/models.rb +129 -0
- data/lib/ruby_llm/protocols/anthropic/streaming.rb +166 -0
- data/lib/ruby_llm/{providers → protocols}/anthropic/tools.rb +43 -34
- data/lib/ruby_llm/protocols/anthropic.rb +100 -0
- data/lib/ruby_llm/protocols/azure/files.rb +16 -0
- data/lib/ruby_llm/protocols/bedrock/async_videos.rb +109 -0
- data/lib/ruby_llm/protocols/bedrock/batches.rb +129 -0
- data/lib/ruby_llm/protocols/bedrock/files.rb +110 -0
- data/lib/ruby_llm/protocols/bedrock/guardrails.rb +122 -0
- data/lib/ruby_llm/protocols/bedrock/rerank.rb +95 -0
- data/lib/ruby_llm/protocols/chat_completions/batches.rb +32 -0
- data/lib/ruby_llm/protocols/chat_completions/chat.rb +493 -0
- data/lib/ruby_llm/protocols/chat_completions/embedding_batches.rb +35 -0
- data/lib/ruby_llm/protocols/chat_completions/embeddings.rb +60 -0
- data/lib/ruby_llm/protocols/chat_completions/images.rb +127 -0
- data/lib/ruby_llm/protocols/chat_completions/media.rb +121 -0
- data/lib/ruby_llm/protocols/chat_completions/models.rb +39 -0
- data/lib/ruby_llm/protocols/chat_completions/moderation.rb +52 -0
- data/lib/ruby_llm/protocols/chat_completions/rerank.rb +56 -0
- data/lib/ruby_llm/protocols/chat_completions/speech.rb +40 -0
- data/lib/ruby_llm/protocols/chat_completions/streaming.rb +69 -0
- data/lib/ruby_llm/{providers/openai → protocols/chat_completions}/tools.rb +17 -17
- data/lib/ruby_llm/protocols/chat_completions/transcription.rb +150 -0
- data/lib/ruby_llm/protocols/chat_completions.rb +21 -0
- data/lib/ruby_llm/protocols/cohere/batch_requests.rb +75 -0
- data/lib/ruby_llm/protocols/cohere/batches.rb +98 -0
- data/lib/ruby_llm/protocols/cohere/chat.rb +227 -0
- data/lib/ruby_llm/protocols/cohere/datasets.rb +102 -0
- data/lib/ruby_llm/protocols/cohere/embeddings.rb +68 -0
- data/lib/ruby_llm/protocols/cohere/media.rb +77 -0
- data/lib/ruby_llm/protocols/cohere/models.rb +92 -0
- data/lib/ruby_llm/protocols/cohere/ocr.rb +63 -0
- data/lib/ruby_llm/protocols/cohere/rerank.rb +52 -0
- data/lib/ruby_llm/protocols/cohere/streaming.rb +105 -0
- data/lib/ruby_llm/protocols/cohere/tokenization.rb +22 -0
- data/lib/ruby_llm/protocols/cohere/tools.rb +132 -0
- data/lib/ruby_llm/protocols/cohere/transcription.rb +41 -0
- data/lib/ruby_llm/protocols/cohere.rb +21 -0
- data/lib/ruby_llm/protocols/converse/batches.rb +55 -0
- data/lib/ruby_llm/protocols/converse/chat.rb +685 -0
- data/lib/ruby_llm/protocols/converse/media.rb +178 -0
- data/lib/ruby_llm/protocols/converse/streaming.rb +424 -0
- data/lib/ruby_llm/protocols/converse.rb +54 -0
- data/lib/ruby_llm/protocols/deepgram/models.rb +74 -0
- data/lib/ruby_llm/protocols/deepgram/speech.rb +93 -0
- data/lib/ruby_llm/protocols/deepgram/streaming_transcription.rb +89 -0
- data/lib/ruby_llm/protocols/deepgram/transcription.rb +96 -0
- data/lib/ruby_llm/protocols/deepgram.rb +19 -0
- data/lib/ruby_llm/protocols/deepseek/files.rb +31 -0
- data/lib/ruby_llm/protocols/elevenlabs/assets.rb +36 -0
- data/lib/ruby_llm/protocols/elevenlabs/flows/images.rb +74 -0
- data/lib/ruby_llm/protocols/elevenlabs/flows/media.rb +42 -0
- data/lib/ruby_llm/protocols/elevenlabs/flows/videos.rb +93 -0
- data/lib/ruby_llm/protocols/elevenlabs/flows.rb +14 -0
- data/lib/ruby_llm/protocols/elevenlabs/models.rb +64 -0
- data/lib/ruby_llm/protocols/elevenlabs/speech.rb +67 -0
- data/lib/ruby_llm/protocols/elevenlabs/streaming_transcription.rb +127 -0
- data/lib/ruby_llm/protocols/elevenlabs/transcription.rb +61 -0
- data/lib/ruby_llm/protocols/elevenlabs.rb +15 -0
- data/lib/ruby_llm/protocols/files.rb +119 -0
- data/lib/ruby_llm/protocols/gemini/batches.rb +162 -0
- data/lib/ruby_llm/protocols/gemini/caches.rb +59 -0
- data/lib/ruby_llm/protocols/gemini/chat.rb +453 -0
- data/lib/ruby_llm/protocols/gemini/embedding_batches.rb +86 -0
- data/lib/ruby_llm/protocols/gemini/embeddings.rb +70 -0
- data/lib/ruby_llm/protocols/gemini/file_transcription.rb +32 -0
- data/lib/ruby_llm/protocols/gemini/files.rb +115 -0
- data/lib/ruby_llm/protocols/gemini/images.rb +183 -0
- data/lib/ruby_llm/protocols/gemini/live_transcription.rb +140 -0
- data/lib/ruby_llm/{providers → protocols}/gemini/media.rb +33 -20
- data/lib/ruby_llm/protocols/gemini/models.rb +71 -0
- data/lib/ruby_llm/protocols/gemini/speech.rb +56 -0
- data/lib/ruby_llm/protocols/gemini/streaming.rb +96 -0
- data/lib/ruby_llm/protocols/gemini/tools.rb +157 -0
- data/lib/ruby_llm/{providers → protocols}/gemini/transcription.rb +22 -22
- data/lib/ruby_llm/protocols/gemini/videos.rb +103 -0
- data/lib/ruby_llm/protocols/gemini.rb +35 -0
- data/lib/ruby_llm/protocols/gpustack/responses.rb +111 -0
- data/lib/ruby_llm/protocols/gpustack/tokenization.rb +22 -0
- data/lib/ruby_llm/protocols/gpustack/videos.rb +96 -0
- data/lib/ruby_llm/protocols/interactions/chat.rb +145 -0
- data/lib/ruby_llm/protocols/interactions/content.rb +90 -0
- data/lib/ruby_llm/protocols/interactions/streaming.rb +91 -0
- data/lib/ruby_llm/protocols/interactions/tools.rb +51 -0
- data/lib/ruby_llm/protocols/interactions/transcription.rb +58 -0
- data/lib/ruby_llm/protocols/interactions.rb +29 -0
- data/lib/ruby_llm/protocols/invoke_model/cohere_embeddings.rb +51 -0
- data/lib/ruby_llm/protocols/invoke_model/embedding_batches.rb +111 -0
- data/lib/ruby_llm/protocols/invoke_model/nova_embeddings.rb +50 -0
- data/lib/ruby_llm/protocols/invoke_model/stability_images.rb +103 -0
- data/lib/ruby_llm/protocols/invoke_model/titan_multimodal_embeddings.rb +33 -0
- data/lib/ruby_llm/protocols/invoke_model/titan_text_embeddings.rb +44 -0
- data/lib/ruby_llm/protocols/invoke_model.rb +57 -0
- data/lib/ruby_llm/protocols/mistral/content.rb +49 -0
- data/lib/ruby_llm/protocols/mistral/conversations/chat.rb +160 -0
- data/lib/ruby_llm/protocols/mistral/conversations/images.rb +43 -0
- data/lib/ruby_llm/protocols/mistral/conversations/streaming.rb +83 -0
- data/lib/ruby_llm/protocols/mistral/conversations.rb +30 -0
- data/lib/ruby_llm/protocols/mistral/files.rb +36 -0
- data/lib/ruby_llm/protocols/mistral/multi_completion.rb +160 -0
- data/lib/ruby_llm/protocols/openai/batches.rb +126 -0
- data/lib/ruby_llm/protocols/openai/files.rb +42 -0
- data/lib/ruby_llm/protocols/openrouter/batches.rb +147 -0
- data/lib/ruby_llm/protocols/openrouter/files.rb +24 -0
- data/lib/ruby_llm/protocols/openrouter/responses.rb +53 -0
- data/lib/ruby_llm/protocols/openrouter/transcription.rb +51 -0
- data/lib/ruby_llm/protocols/perplexity/files.rb +48 -0
- data/lib/ruby_llm/protocols/perplexity/router.rb +59 -0
- data/lib/ruby_llm/protocols/responses/approvals.rb +32 -0
- data/lib/ruby_llm/protocols/responses/batches.rb +32 -0
- data/lib/ruby_llm/protocols/responses/chat.rb +476 -0
- data/lib/ruby_llm/protocols/responses/compaction.rb +29 -0
- data/lib/ruby_llm/protocols/responses/media.rb +61 -0
- data/lib/ruby_llm/protocols/responses/streaming.rb +117 -0
- data/lib/ruby_llm/protocols/responses/token_counting.rb +26 -0
- data/lib/ruby_llm/protocols/responses/tools.rb +39 -0
- data/lib/ruby_llm/protocols/responses.rb +35 -0
- data/lib/ruby_llm/protocols/vertexai/batch_prediction.rb +155 -0
- data/lib/ruby_llm/protocols/vertexai/embedding_prediction/requests.rb +85 -0
- data/lib/ruby_llm/protocols/vertexai/embedding_prediction/results.rb +74 -0
- data/lib/ruby_llm/protocols/vertexai/embedding_prediction.rb +56 -0
- data/lib/ruby_llm/protocols/vertexai/files.rb +101 -0
- data/lib/ruby_llm/protocols/vertexai/ranking.rb +69 -0
- data/lib/ruby_llm/protocols/vertexai/research.rb +193 -0
- data/lib/ruby_llm/protocols/xai/files.rb +30 -0
- data/lib/ruby_llm/protocols/xai/streaming_transcription.rb +120 -0
- data/lib/ruby_llm/protocols/xai/tokenization.rb +23 -0
- data/lib/ruby_llm/provider.rb +565 -124
- data/lib/ruby_llm/providers/anthropic/capabilities.rb +5 -7
- data/lib/ruby_llm/providers/anthropic.rb +4 -6
- data/lib/ruby_llm/providers/azure/audio.rb +18 -0
- data/lib/ruby_llm/providers/azure/capabilities.rb +16 -0
- data/lib/ruby_llm/providers/azure/chat.rb +2 -9
- data/lib/ruby_llm/providers/azure/chat_completions/batches.rb +29 -0
- data/lib/ruby_llm/providers/azure/chat_completions.rb +80 -0
- data/lib/ruby_llm/providers/azure/cohere.rb +33 -0
- data/lib/ruby_llm/providers/azure/embeddings.rb +3 -2
- data/lib/ruby_llm/providers/azure/images.rb +22 -0
- data/lib/ruby_llm/providers/azure/media.rb +6 -15
- data/lib/ruby_llm/providers/azure/models.rb +35 -0
- data/lib/ruby_llm/providers/azure/responses.rb +26 -0
- data/lib/ruby_llm/providers/azure/videos.rb +64 -0
- data/lib/ruby_llm/providers/azure.rb +77 -78
- data/lib/ruby_llm/providers/bedrock/auth.rb +61 -41
- data/lib/ruby_llm/providers/bedrock/capabilities.rb +18 -0
- data/lib/ruby_llm/providers/bedrock/mantle/anthropic.rb +39 -0
- data/lib/ruby_llm/providers/bedrock/mantle/chat_completions.rb +23 -0
- data/lib/ruby_llm/providers/bedrock/mantle/responses.rb +24 -0
- data/lib/ruby_llm/providers/bedrock/mantle/voxtral.rb +96 -0
- data/lib/ruby_llm/providers/bedrock/mantle.rb +57 -0
- data/lib/ruby_llm/providers/bedrock/models.rb +194 -42
- data/lib/ruby_llm/providers/bedrock.rb +217 -46
- data/lib/ruby_llm/providers/cohere.rb +31 -0
- data/lib/ruby_llm/providers/deepgram.rb +37 -0
- data/lib/ruby_llm/providers/deepseek/capabilities.rb +4 -8
- data/lib/ruby_llm/providers/deepseek/chat.rb +56 -0
- data/lib/ruby_llm/providers/deepseek/responses.rb +68 -0
- data/lib/ruby_llm/providers/deepseek.rb +9 -2
- data/lib/ruby_llm/providers/elevenlabs.rb +35 -0
- data/lib/ruby_llm/providers/gemini/capabilities.rb +8 -107
- data/lib/ruby_llm/providers/gemini.rb +15 -8
- data/lib/ruby_llm/providers/gpustack/chat.rb +2 -9
- data/lib/ruby_llm/providers/gpustack/embeddings.rb +28 -0
- data/lib/ruby_llm/providers/gpustack/media.rb +17 -16
- data/lib/ruby_llm/providers/gpustack/models.rb +72 -60
- data/lib/ruby_llm/providers/gpustack/speech.rb +15 -0
- data/lib/ruby_llm/providers/gpustack/transcription.rb +29 -0
- data/lib/ruby_llm/providers/gpustack.rb +35 -11
- data/lib/ruby_llm/providers/mistral/capabilities.rb +7 -155
- data/lib/ruby_llm/providers/mistral/chat.rb +37 -61
- data/lib/ruby_llm/providers/mistral/chat_completions/batches.rb +120 -0
- data/lib/ruby_llm/providers/mistral/chat_completions.rb +21 -0
- data/lib/ruby_llm/providers/mistral/conversations.rb +12 -0
- data/lib/ruby_llm/providers/mistral/embeddings.rb +6 -4
- data/lib/ruby_llm/providers/mistral/media.rb +43 -0
- data/lib/ruby_llm/providers/mistral/models.rb +57 -21
- data/lib/ruby_llm/providers/mistral/ocr.rb +47 -0
- data/lib/ruby_llm/providers/mistral/speech.rb +51 -0
- data/lib/ruby_llm/providers/mistral/transcription.rb +62 -0
- data/lib/ruby_llm/providers/mistral.rb +18 -6
- data/lib/ruby_llm/providers/ollama/chat.rb +9 -8
- data/lib/ruby_llm/providers/ollama/media.rb +6 -15
- data/lib/ruby_llm/providers/ollama/models.rb +50 -9
- data/lib/ruby_llm/providers/ollama.rb +9 -8
- data/lib/ruby_llm/providers/ollama_cloud/models.rb +14 -0
- data/lib/ruby_llm/providers/ollama_cloud.rb +40 -0
- data/lib/ruby_llm/providers/openai/capabilities.rb +54 -259
- data/lib/ruby_llm/providers/openai/models.rb +23 -23
- data/lib/ruby_llm/providers/openai/responses.rb +13 -0
- data/lib/ruby_llm/providers/openai.rb +92 -11
- data/lib/ruby_llm/providers/openrouter/chat.rb +130 -104
- data/lib/ruby_llm/providers/openrouter/embeddings.rb +51 -0
- data/lib/ruby_llm/providers/openrouter/images.rb +44 -43
- data/lib/ruby_llm/providers/openrouter/media.rb +34 -0
- data/lib/ruby_llm/providers/openrouter/models.rb +50 -11
- data/lib/ruby_llm/providers/openrouter/speech.rb +32 -0
- data/lib/ruby_llm/providers/openrouter/streaming.rb +31 -38
- data/lib/ruby_llm/providers/openrouter/videos.rb +81 -0
- data/lib/ruby_llm/providers/openrouter.rb +78 -20
- data/lib/ruby_llm/providers/perplexity/chat.rb +4 -0
- data/lib/ruby_llm/providers/perplexity/embeddings.rb +32 -0
- data/lib/ruby_llm/providers/perplexity/media.rb +46 -0
- data/lib/ruby_llm/providers/perplexity/models.rb +80 -13
- data/lib/ruby_llm/providers/perplexity.rb +29 -21
- data/lib/ruby_llm/providers/vertexai/anthropic/batches.rb +52 -0
- data/lib/ruby_llm/providers/vertexai/anthropic.rb +34 -0
- data/lib/ruby_llm/providers/vertexai/capabilities.rb +19 -0
- data/lib/ruby_llm/providers/vertexai/chat_completions/batches.rb +54 -0
- data/lib/ruby_llm/providers/vertexai/chat_completions.rb +15 -0
- data/lib/ruby_llm/providers/vertexai/embed_content.rb +42 -0
- data/lib/ruby_llm/providers/vertexai/embeddings.rb +22 -7
- data/lib/ruby_llm/providers/vertexai/gemini/batches.rb +42 -0
- data/lib/ruby_llm/providers/vertexai/gemini.rb +69 -0
- data/lib/ruby_llm/providers/vertexai/live_transcription.rb +24 -0
- data/lib/ruby_llm/providers/vertexai/mistral.rb +28 -0
- data/lib/ruby_llm/providers/vertexai/models.rb +145 -43
- data/lib/ruby_llm/providers/vertexai/transcription.rb +49 -4
- data/lib/ruby_llm/providers/vertexai/videos.rb +61 -0
- data/lib/ruby_llm/providers/vertexai.rb +164 -17
- data/lib/ruby_llm/providers/xai/capabilities.rb +18 -0
- data/lib/ruby_llm/providers/xai/chat.rb +10 -0
- data/lib/ruby_llm/providers/xai/chat_completions/batches.rb +108 -0
- data/lib/ruby_llm/providers/xai/chat_completions.rb +19 -0
- data/lib/ruby_llm/providers/xai/images.rb +91 -0
- data/lib/ruby_llm/providers/xai/models.rb +32 -48
- data/lib/ruby_llm/providers/xai/reported_cost.rb +18 -0
- data/lib/ruby_llm/providers/xai/responses.rb +52 -0
- data/lib/ruby_llm/providers/xai/speech.rb +45 -0
- data/lib/ruby_llm/providers/xai/transcription.rb +48 -0
- data/lib/ruby_llm/providers/xai/videos.rb +87 -0
- data/lib/ruby_llm/providers/xai.rb +17 -7
- data/lib/ruby_llm/railtie.rb +11 -16
- data/lib/ruby_llm/rerank.rb +105 -0
- data/lib/ruby_llm/research_job.rb +241 -0
- data/lib/ruby_llm/search_results.rb +68 -0
- data/lib/ruby_llm/server_tool_call.rb +73 -0
- data/lib/ruby_llm/speech.rb +159 -0
- data/lib/ruby_llm/speech_chunk.rb +33 -0
- data/lib/ruby_llm/support/deprecator.rb +22 -0
- data/lib/ruby_llm/support/inspectable.rb +49 -0
- data/lib/ruby_llm/support/instrumentation.rb +41 -0
- data/lib/ruby_llm/support/utils.rb +147 -0
- data/lib/ruby_llm/thinking.rb +127 -20
- data/lib/ruby_llm/tokenization.rb +59 -0
- data/lib/ruby_llm/tokens.rb +103 -33
- data/lib/ruby_llm/tool.rb +266 -91
- data/lib/ruby_llm/tool_call.rb +36 -3
- data/lib/ruby_llm/tools/server_tools.rb +109 -0
- data/lib/ruby_llm/transcription/wav_audio.rb +62 -0
- data/lib/ruby_llm/transcription.rb +139 -13
- data/lib/ruby_llm/transcription_chunk.rb +68 -0
- data/lib/ruby_llm/transport/connection.rb +193 -0
- data/lib/ruby_llm/transport/error_middleware.rb +131 -0
- data/lib/ruby_llm/transport/usage_middleware.rb +28 -0
- data/lib/ruby_llm/transport/websocket_connection.rb +220 -0
- data/lib/ruby_llm/uploaded_file.rb +144 -0
- data/lib/ruby_llm/version.rb +2 -1
- data/lib/ruby_llm/video.rb +136 -0
- data/lib/ruby_llm/video_job.rb +150 -0
- data/lib/ruby_llm/workflow.rb +91 -0
- data/lib/ruby_llm.rb +385 -4
- data/lib/tasks/ruby_llm.rake +21 -16
- data/skills/rubyllm/SKILL.md +81 -0
- data/skills/rubyllm/agents/openai.yaml +4 -0
- metadata +340 -92
- data/lib/generators/ruby_llm/install/templates/add_references_to_chats_tool_calls_and_messages_migration.rb.tt +0 -9
- data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +0 -39
- data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +0 -21
- data/lib/generators/ruby_llm/install/templates/model_model.rb.tt +0 -3
- data/lib/generators/ruby_llm/install/templates/tool_call_model.rb.tt +0 -3
- data/lib/generators/ruby_llm/upgrade_to_v1_10/templates/add_v1_10_message_columns.rb.tt +0 -19
- data/lib/generators/ruby_llm/upgrade_to_v1_10/upgrade_to_v1_10_generator.rb +0 -50
- data/lib/generators/ruby_llm/upgrade_to_v1_14/templates/add_v1_14_tool_call_columns.rb.tt +0 -7
- data/lib/generators/ruby_llm/upgrade_to_v1_14/upgrade_to_v1_14_generator.rb +0 -49
- data/lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt +0 -145
- data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +0 -122
- data/lib/generators/ruby_llm/upgrade_to_v1_9/templates/add_v1_9_message_columns.rb.tt +0 -15
- data/lib/generators/ruby_llm/upgrade_to_v1_9/upgrade_to_v1_9_generator.rb +0 -49
- data/lib/ruby_llm/active_record/acts_as_legacy.rb +0 -530
- data/lib/ruby_llm/active_record/model_methods.rb +0 -82
- data/lib/ruby_llm/active_record/tool_call_methods.rb +0 -18
- data/lib/ruby_llm/aliases.rb +0 -38
- data/lib/ruby_llm/connection.rb +0 -130
- data/lib/ruby_llm/content.rb +0 -77
- data/lib/ruby_llm/mime_type.rb +0 -71
- data/lib/ruby_llm/model/info.rb +0 -130
- data/lib/ruby_llm/models_schema.json +0 -171
- data/lib/ruby_llm/providers/anthropic/chat.rb +0 -257
- data/lib/ruby_llm/providers/anthropic/content.rb +0 -44
- data/lib/ruby_llm/providers/anthropic/embeddings.rb +0 -20
- data/lib/ruby_llm/providers/anthropic/media.rb +0 -92
- data/lib/ruby_llm/providers/anthropic/models.rb +0 -57
- data/lib/ruby_llm/providers/anthropic/streaming.rb +0 -69
- data/lib/ruby_llm/providers/bedrock/chat.rb +0 -403
- data/lib/ruby_llm/providers/bedrock/media.rb +0 -90
- data/lib/ruby_llm/providers/bedrock/streaming.rb +0 -322
- data/lib/ruby_llm/providers/gemini/chat.rb +0 -543
- data/lib/ruby_llm/providers/gemini/embeddings.rb +0 -37
- data/lib/ruby_llm/providers/gemini/images.rb +0 -47
- data/lib/ruby_llm/providers/gemini/models.rb +0 -38
- data/lib/ruby_llm/providers/gemini/streaming.rb +0 -96
- data/lib/ruby_llm/providers/gemini/tools.rb +0 -232
- data/lib/ruby_llm/providers/gpustack/capabilities.rb +0 -20
- data/lib/ruby_llm/providers/ollama/capabilities.rb +0 -20
- data/lib/ruby_llm/providers/openai/chat.rb +0 -221
- data/lib/ruby_llm/providers/openai/embeddings.rb +0 -33
- data/lib/ruby_llm/providers/openai/images.rb +0 -90
- data/lib/ruby_llm/providers/openai/media.rb +0 -84
- data/lib/ruby_llm/providers/openai/moderation.rb +0 -34
- data/lib/ruby_llm/providers/openai/streaming.rb +0 -53
- data/lib/ruby_llm/providers/openai/temperature.rb +0 -28
- data/lib/ruby_llm/providers/openai/transcription.rb +0 -70
- data/lib/ruby_llm/providers/perplexity/capabilities.rb +0 -72
- data/lib/ruby_llm/providers/vertexai/chat.rb +0 -14
- data/lib/ruby_llm/providers/vertexai/streaming.rb +0 -14
- data/lib/ruby_llm/stream_accumulator.rb +0 -203
- data/lib/ruby_llm/streaming.rb +0 -175
- data/lib/ruby_llm/utils.rb +0 -91
- data/lib/tasks/models.rake +0 -565
- data/lib/tasks/release.rake +0 -67
- data/lib/tasks/vcr.rake +0 -124
data/lib/ruby_llm/agent.rb
CHANGED
|
@@ -1,88 +1,383 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'erb'
|
|
4
3
|
require 'forwardable'
|
|
5
|
-
require '
|
|
6
|
-
require 'ruby_llm/schema'
|
|
4
|
+
require 'schematist'
|
|
7
5
|
|
|
8
6
|
module RubyLLM
|
|
9
|
-
#
|
|
7
|
+
# An Agent is a reusable chat configuration defined as a class. Subclasses
|
|
8
|
+
# declare a model, instructions, tools, and other settings once, then build
|
|
9
|
+
# configured chats wherever they are needed.
|
|
10
|
+
#
|
|
11
|
+
# class SupportAgent < RubyLLM::Agent
|
|
12
|
+
# model "gpt-5.6-luna"
|
|
13
|
+
# instructions "You are a concise support assistant."
|
|
14
|
+
# tools SearchDocs, LookupAccount
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# SupportAgent.new.ask "How do I reset my API key?"
|
|
18
|
+
#
|
|
19
|
+
# ::chat returns a configured Chat. When ::chat_model names an ActiveRecord
|
|
20
|
+
# chat class, ::create, ::create!, and ::find return configured records of
|
|
21
|
+
# that class instead.
|
|
22
|
+
#
|
|
23
|
+
# Configuration that depends on runtime state goes in blocks or lambdas.
|
|
24
|
+
# They are evaluated when a chat is built, with +chat+ and any declared
|
|
25
|
+
# ::inputs available as methods:
|
|
26
|
+
#
|
|
27
|
+
# class WorkAssistant < RubyLLM::Agent
|
|
28
|
+
# inputs :workspace
|
|
29
|
+
#
|
|
30
|
+
# instructions { "You are helping #{workspace.name}" }
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
# WorkAssistant.chat(workspace: workspace)
|
|
34
|
+
#
|
|
35
|
+
# Agent instances delegate Chat's conversation API (#ask, #complete,
|
|
36
|
+
# #with_tools, and so on) to the wrapped chat, which is available via
|
|
37
|
+
# #chat. Direct transcript replacement stays on the wrapped chat because
|
|
38
|
+
# Rails-backed chat models own their message association. Agents are
|
|
39
|
+
# enumerable over their messages.
|
|
10
40
|
class Agent
|
|
11
41
|
extend Forwardable
|
|
12
42
|
include Enumerable
|
|
13
43
|
|
|
44
|
+
DUPED_INHERITED_CONFIG = {
|
|
45
|
+
:@chat_kwargs => {},
|
|
46
|
+
:@tools => [],
|
|
47
|
+
:@server_tools => [],
|
|
48
|
+
:@tool_options => {},
|
|
49
|
+
:@caching => nil,
|
|
50
|
+
:@compaction => nil,
|
|
51
|
+
:@provider_options => {},
|
|
52
|
+
:@headers => {},
|
|
53
|
+
:@input_names => [],
|
|
54
|
+
:@fallbacks => [],
|
|
55
|
+
:@fallback_options => {},
|
|
56
|
+
:@rescue_handlers => [],
|
|
57
|
+
:@instructions => []
|
|
58
|
+
}.freeze
|
|
59
|
+
# Simple value options: a class-level getter/setter macro whose value the
|
|
60
|
+
# agent forwards to the matching Chat#with_* when it builds its chat.
|
|
61
|
+
PASSTHROUGH_OPTIONS = %i[temperature max_output_tokens].freeze
|
|
62
|
+
THINKING_OPTIONS = %i[effort budget display].freeze
|
|
63
|
+
|
|
64
|
+
# The chat operations an agent instance runs through its ::rescue_from
|
|
65
|
+
# handlers. Remaining delegated methods pass through untouched.
|
|
66
|
+
GUARDED_OPERATIONS = %i[ask say ask_later complete generate run_tools step count_tokens compact].freeze
|
|
67
|
+
|
|
68
|
+
# Chat methods that return the wrapped chat so calls can be chained there.
|
|
69
|
+
CHAINABLE_CHAT_DELEGATES = %i[
|
|
70
|
+
with_instructions with_tools with_server_tools with_tool_options with_model
|
|
71
|
+
with_temperature with_max_output_tokens with_thinking with_citations
|
|
72
|
+
with_end_user with_compaction with_caching with_context with_provider_options
|
|
73
|
+
with_headers with_schema with_fallbacks
|
|
74
|
+
before_request before_message after_message before_tool_call after_tool_result
|
|
75
|
+
before_fallback after_fallback
|
|
76
|
+
cancel approve deny cache_until_here
|
|
77
|
+
].freeze
|
|
78
|
+
|
|
79
|
+
# Chat values and operations whose return values pass through unchanged.
|
|
80
|
+
PASSTHROUGH_CHAT_DELEGATES = %i[
|
|
81
|
+
model provider messages tools server_tools tool_options provider_options headers schema concurrency
|
|
82
|
+
caching citations compaction context end_user fallbacks thinking temperature max_output_tokens
|
|
83
|
+
each complete? cancelled? awaiting_approval? pending_approvals
|
|
84
|
+
add_message add_completion tokens cost render
|
|
85
|
+
].freeze
|
|
86
|
+
|
|
87
|
+
COPIED_INHERITED_CONFIG = (%i[
|
|
88
|
+
@thinking
|
|
89
|
+
@citations
|
|
90
|
+
@end_user
|
|
91
|
+
@schema
|
|
92
|
+
@context
|
|
93
|
+
@chat_model
|
|
94
|
+
] + PASSTHROUGH_OPTIONS.map { |option| :"@#{option}" }).freeze
|
|
95
|
+
private_constant :DUPED_INHERITED_CONFIG, :COPIED_INHERITED_CONFIG,
|
|
96
|
+
:PASSTHROUGH_OPTIONS, :THINKING_OPTIONS, :GUARDED_OPERATIONS,
|
|
97
|
+
:CHAINABLE_CHAT_DELEGATES, :PASSTHROUGH_CHAT_DELEGATES
|
|
98
|
+
|
|
14
99
|
class << self
|
|
15
|
-
def inherited(subclass)
|
|
100
|
+
def inherited(subclass) # :nodoc:
|
|
16
101
|
super
|
|
17
|
-
subclass
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
102
|
+
copy_inherited_config_to(subclass)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Sets the model used by chats this agent builds. Extra +options+ are
|
|
106
|
+
# forwarded to RubyLLM.chat, including +provider:+ to disambiguate the
|
|
107
|
+
# model and +protocol:+ to override its wire protocol. A block picks
|
|
108
|
+
# the model when the chat is built, with the declared ::inputs
|
|
109
|
+
# available as methods. Called with no arguments, returns the
|
|
110
|
+
# configured chat keywords.
|
|
111
|
+
#
|
|
112
|
+
# model "gpt-5.6-luna"
|
|
113
|
+
# model "gpt-5.6", provider: :openai, protocol: :responses
|
|
114
|
+
# model { quality == :high ? "gpt-5.6" : "gpt-5.6-luna" }
|
|
115
|
+
#
|
|
116
|
+
# The block runs before the chat exists, so it can read inputs but not
|
|
117
|
+
# +chat+.
|
|
118
|
+
def model(model_id = nil, **options, &block)
|
|
119
|
+
return @chat_kwargs || {} if model_id.nil? && options.empty? && !block_given?
|
|
120
|
+
|
|
121
|
+
model_value = block || model_id
|
|
122
|
+
options[:model] = model_value unless model_value.nil?
|
|
32
123
|
@chat_kwargs = options
|
|
33
124
|
end
|
|
34
125
|
|
|
126
|
+
# Declares the tools for chats this agent builds. A block defers
|
|
127
|
+
# construction until the chat is built. Configure how the model uses
|
|
128
|
+
# them with ::tool_options. Called with no arguments, returns the
|
|
129
|
+
# declared tools.
|
|
130
|
+
#
|
|
131
|
+
# tools SearchDocs, LookupAccount
|
|
132
|
+
# tools { [TodoTool.new(chat: chat)] }
|
|
133
|
+
#
|
|
35
134
|
def tools(*tools, &block)
|
|
36
135
|
return @tools || [] if tools.empty? && !block_given?
|
|
37
136
|
|
|
38
137
|
@tools = block_given? ? block : tools.flatten
|
|
39
138
|
end
|
|
40
139
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
140
|
+
# Sets how chats this agent builds use their tools, applied via
|
|
141
|
+
# Chat#with_tool_options. Accepts +choice:+, +calls:+, and
|
|
142
|
+
# +concurrency:+. A block defers evaluation until the chat is built.
|
|
143
|
+
# Called with no arguments, returns the configured options.
|
|
144
|
+
#
|
|
145
|
+
# tool_options choice: :required, calls: :one
|
|
146
|
+
#
|
|
147
|
+
def tool_options(**options, &block)
|
|
148
|
+
return @tool_options || {} if options.empty? && !block_given?
|
|
149
|
+
|
|
150
|
+
@tool_options = block_given? ? block : options
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Enables provider-executed tools for chats this agent builds, applied
|
|
154
|
+
# via Chat#with_server_tools. Accepts the same aliases, options, and
|
|
155
|
+
# raw Hashes; a block defers evaluation until the chat is built.
|
|
156
|
+
# Called with no arguments, returns the declared entries.
|
|
157
|
+
#
|
|
158
|
+
# server_tools :web_search
|
|
159
|
+
# server_tools web_search: { allowed_domains: ["ruby-lang.org"] }
|
|
160
|
+
#
|
|
161
|
+
def server_tools(*tools, **tools_with_options, &block)
|
|
162
|
+
return @server_tools || [] if tools.empty? && tools_with_options.empty? && !block_given?
|
|
163
|
+
|
|
164
|
+
@server_tools = block_given? ? block : RubyLLM::Tools::ServerTools.normalize(tools, tools_with_options)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Adds system instructions for chats this agent builds. Accepts a string,
|
|
168
|
+
# a block evaluated when the chat is built, or keyword locals for the
|
|
169
|
+
# agent's conventional prompt template (for a WorkAssistant agent,
|
|
170
|
+
# <tt>app/prompts/work_assistant/instructions.txt.erb</tt>). Multiple
|
|
171
|
+
# declarations are applied in order.
|
|
172
|
+
#
|
|
173
|
+
# instructions "You are a helpful assistant."
|
|
174
|
+
# instructions { "You are helping #{workspace.name}" }
|
|
175
|
+
# instructions display_name: -> { chat.user.display_name_or_email }
|
|
176
|
+
# instructions append: true, persist: false do
|
|
177
|
+
# "Today is #{Date.current}"
|
|
178
|
+
# end
|
|
179
|
+
#
|
|
180
|
+
# A named agent uses its conventional template automatically when it
|
|
181
|
+
# exists, even without calling this method. In Rails mode, declarations
|
|
182
|
+
# persist when the record is created unless <tt>persist: false</tt>;
|
|
183
|
+
# ::find always reapplies them without rewriting history. Called with no
|
|
184
|
+
# arguments, returns the declarations.
|
|
185
|
+
def instructions(text = nil, append: false, persist: true, cache_until_here: false, **prompt_locals, &block)
|
|
186
|
+
return instruction_declarations if text.nil? && prompt_locals.empty? && !block_given?
|
|
187
|
+
|
|
188
|
+
instruction_declarations << {
|
|
189
|
+
value: block || text || { prompt: 'instructions', locals: prompt_locals },
|
|
190
|
+
append: append,
|
|
191
|
+
persist: persist,
|
|
192
|
+
cache_until_here: cache_until_here
|
|
193
|
+
}
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
##
|
|
197
|
+
# :method: temperature
|
|
198
|
+
# :call-seq: temperature(value = nil)
|
|
199
|
+
#
|
|
200
|
+
# Sets the sampling temperature for chats this agent builds. Called
|
|
201
|
+
# with no argument, returns the configured value.
|
|
202
|
+
#
|
|
203
|
+
# temperature 0.2
|
|
204
|
+
|
|
205
|
+
##
|
|
206
|
+
# :method: max_output_tokens
|
|
207
|
+
# :call-seq: max_output_tokens(value = nil)
|
|
208
|
+
#
|
|
209
|
+
# Caps the number of tokens chats this agent builds may generate.
|
|
210
|
+
# Called with no argument, returns the configured value.
|
|
211
|
+
#
|
|
212
|
+
# max_output_tokens 1000
|
|
213
|
+
|
|
214
|
+
PASSTHROUGH_OPTIONS.each do |option|
|
|
215
|
+
define_method(option) do |value = nil|
|
|
216
|
+
return instance_variable_get(:"@#{option}") if value.nil?
|
|
217
|
+
|
|
218
|
+
instance_variable_set(:"@#{option}", value)
|
|
45
219
|
end
|
|
220
|
+
end
|
|
46
221
|
|
|
47
|
-
|
|
222
|
+
# Enables thinking for chats this agent builds, applied via
|
|
223
|
+
# Chat#with_thinking. With no options, RubyLLM chooses from the model's
|
|
224
|
+
# registered controls. Accepts keywords or an options Hash. Pass +false+
|
|
225
|
+
# to disable it. Passing +nil+ raises ArgumentError.
|
|
226
|
+
#
|
|
227
|
+
# thinking
|
|
228
|
+
# thinking false
|
|
229
|
+
# thinking effort: :low
|
|
230
|
+
# thinking budget: 10_000
|
|
231
|
+
# thinking display: :summarized
|
|
232
|
+
#
|
|
233
|
+
def thinking(enabled = true, **options) # rubocop:disable Style/OptionalBooleanParameter
|
|
234
|
+
return thinking(**enabled.transform_keys(&:to_sym), **options) if enabled.is_a?(Hash)
|
|
235
|
+
|
|
236
|
+
validate_thinking_options(enabled, options)
|
|
237
|
+
@thinking = enabled ? options : false
|
|
48
238
|
end
|
|
49
239
|
|
|
50
|
-
|
|
51
|
-
|
|
240
|
+
# Enables context compaction for chats this agent builds, applied via
|
|
241
|
+
# Chat#with_compaction. With no options, the provider's own defaults
|
|
242
|
+
# apply. Pass +false+ to disable it.
|
|
243
|
+
#
|
|
244
|
+
# compaction
|
|
245
|
+
# compaction false
|
|
246
|
+
# compaction at: 50_000
|
|
247
|
+
#
|
|
248
|
+
def compaction(options = {})
|
|
249
|
+
options = {} if options == true
|
|
250
|
+
unless options == false || options.is_a?(Hash)
|
|
251
|
+
raise ArgumentError, 'compaction accepts true, false, or compaction options'
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
@compaction = options
|
|
255
|
+
end
|
|
52
256
|
|
|
53
|
-
|
|
257
|
+
# Sets the safety identifier for chats this agent builds, applied via
|
|
258
|
+
# Chat#with_end_user. A block defers evaluation until the
|
|
259
|
+
# chat is built, so the id can come from the agent's inputs. Called
|
|
260
|
+
# with no arguments, returns the configured value.
|
|
261
|
+
#
|
|
262
|
+
# end_user "tenant-42"
|
|
263
|
+
# end_user { workspace.public_id }
|
|
264
|
+
#
|
|
265
|
+
def end_user(value = nil, &block)
|
|
266
|
+
return @end_user if value.nil? && !block_given?
|
|
267
|
+
|
|
268
|
+
@end_user = block || value
|
|
54
269
|
end
|
|
55
270
|
|
|
56
|
-
|
|
57
|
-
|
|
271
|
+
# Enables citations for chats this agent builds, applied via
|
|
272
|
+
# Chat#with_citations. Pass +false+ to disable them.
|
|
273
|
+
#
|
|
274
|
+
# citations
|
|
275
|
+
# citations false
|
|
276
|
+
#
|
|
277
|
+
def citations(value = true) # rubocop:disable Style/OptionalBooleanParameter
|
|
278
|
+
raise ArgumentError, 'citations accepts true or false' unless [true, false].include?(value)
|
|
58
279
|
|
|
59
|
-
@
|
|
280
|
+
@citations = value
|
|
60
281
|
end
|
|
61
282
|
|
|
62
|
-
|
|
63
|
-
|
|
283
|
+
# Enables prompt caching for chats this agent builds, applied via
|
|
284
|
+
# Chat#with_caching. With no options, the provider's default behavior
|
|
285
|
+
# applies. Pass +false+ to stop RubyLLM from sending cache controls. A
|
|
286
|
+
# provider may still cache prompts implicitly. A block defers evaluation
|
|
287
|
+
# until the chat is built. Accepts keywords or an options Hash.
|
|
288
|
+
#
|
|
289
|
+
# caching
|
|
290
|
+
# caching false
|
|
291
|
+
# caching ttl: "1h"
|
|
292
|
+
# caching { { ttl: workspace.cache_ttl } }
|
|
293
|
+
#
|
|
294
|
+
def caching(enabled = true, **options, &block) # rubocop:disable Metrics/PerceivedComplexity, Style/OptionalBooleanParameter
|
|
295
|
+
return caching(**enabled.transform_keys(&:to_sym), **options, &block) if enabled.is_a?(Hash)
|
|
296
|
+
|
|
297
|
+
raise ArgumentError, 'caching accepts false or caching options' unless [true, false].include?(enabled)
|
|
298
|
+
raise ArgumentError, 'caching accepts options or a block, not both' if options.any? && block
|
|
299
|
+
raise ArgumentError, 'caching false does not accept options or a block' if !enabled && (options.any? || block)
|
|
300
|
+
|
|
301
|
+
@caching = block || (enabled ? options : false)
|
|
302
|
+
end
|
|
64
303
|
|
|
65
|
-
|
|
304
|
+
# Sets options in the provider's request vocabulary for chats this
|
|
305
|
+
# agent builds, applied via Chat#with_provider_options. A block
|
|
306
|
+
# defers evaluation until the chat is built. Called with no
|
|
307
|
+
# arguments, returns the configured value.
|
|
308
|
+
#
|
|
309
|
+
# provider_options service_tier: "flex"
|
|
310
|
+
#
|
|
311
|
+
def provider_options(**provider_options, &block)
|
|
312
|
+
return @provider_options || {} if provider_options.empty? && !block_given?
|
|
313
|
+
|
|
314
|
+
@provider_options = block_given? ? block : provider_options
|
|
66
315
|
end
|
|
67
316
|
|
|
317
|
+
# Sets custom HTTP headers for chats this agent builds, applied via
|
|
318
|
+
# Chat#with_headers. A block defers evaluation until the chat is
|
|
319
|
+
# built. Called with no arguments, returns the configured value.
|
|
68
320
|
def headers(**headers, &block)
|
|
69
321
|
return @headers || {} if headers.empty? && !block_given?
|
|
70
322
|
|
|
71
323
|
@headers = block_given? ? block : headers
|
|
72
324
|
end
|
|
73
325
|
|
|
326
|
+
# Sets the structured output schema for chats this agent builds,
|
|
327
|
+
# applied via Chat#with_schema. Accepts a schema class, a JSON schema
|
|
328
|
+
# hash, or a block. A plain block is built with the Schematist::Schema
|
|
329
|
+
# DSL; a lambda is evaluated when the chat is built. Called with no
|
|
330
|
+
# arguments, returns the configured value.
|
|
331
|
+
#
|
|
332
|
+
# schema PersonSchema
|
|
333
|
+
# schema do
|
|
334
|
+
# string :verdict, enum: ["pass", "revise"]
|
|
335
|
+
# string :feedback
|
|
336
|
+
# end
|
|
337
|
+
#
|
|
74
338
|
def schema(value = nil, &block)
|
|
75
339
|
return @schema if value.nil? && !block_given?
|
|
76
340
|
|
|
77
341
|
@schema = block_given? ? block : value
|
|
78
342
|
end
|
|
79
343
|
|
|
344
|
+
# Sets fallback models for chats this agent builds, applied via
|
|
345
|
+
# Chat#with_fallbacks. Called with no arguments, returns the
|
|
346
|
+
# configured models.
|
|
347
|
+
#
|
|
348
|
+
# fallbacks "gpt-4.1-mini", "claude-haiku-4-5"
|
|
349
|
+
# fallbacks "gpt-4.1-mini", on: [RubyLLM::RateLimitError]
|
|
350
|
+
#
|
|
351
|
+
def fallbacks(*models, **options)
|
|
352
|
+
return @fallbacks || [] if models.empty? && options.empty?
|
|
353
|
+
raise ArgumentError, 'To set fallback options, provide at least one fallback model' if models.empty?
|
|
354
|
+
|
|
355
|
+
@fallbacks = models.flatten.compact
|
|
356
|
+
@fallback_options = options
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
def fallback_options
|
|
360
|
+
@fallback_options || {}
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
private :fallback_options
|
|
364
|
+
|
|
365
|
+
# Sets a Context whose configuration chats this agent builds should
|
|
366
|
+
# use, applied via Chat#with_context. Called with no argument, returns
|
|
367
|
+
# the configured context.
|
|
80
368
|
def context(value = nil)
|
|
81
369
|
return @context if value.nil?
|
|
82
370
|
|
|
83
371
|
@context = value
|
|
84
372
|
end
|
|
85
373
|
|
|
374
|
+
# Sets the ActiveRecord chat class this agent creates and finds,
|
|
375
|
+
# activating Rails mode (::create, ::create!, ::find, and
|
|
376
|
+
# ::sync_instructions). Accepts the class or its name as a string.
|
|
377
|
+
# Called with no argument, returns the configured value.
|
|
378
|
+
#
|
|
379
|
+
# chat_model Chat
|
|
380
|
+
#
|
|
86
381
|
def chat_model(value = nil)
|
|
87
382
|
return @chat_model if value.nil?
|
|
88
383
|
|
|
@@ -90,31 +385,126 @@ module RubyLLM
|
|
|
90
385
|
remove_instance_variable(:@resolved_chat_model) if instance_variable_defined?(:@resolved_chat_model)
|
|
91
386
|
end
|
|
92
387
|
|
|
388
|
+
# Declares named runtime inputs. Matching keyword arguments passed to
|
|
389
|
+
# ::chat, ::create, ::create!, ::find, or ::new become methods inside
|
|
390
|
+
# lazy configuration blocks. Called with no arguments, returns the
|
|
391
|
+
# declared names.
|
|
392
|
+
#
|
|
393
|
+
# inputs :workspace
|
|
394
|
+
#
|
|
93
395
|
def inputs(*names)
|
|
94
396
|
return @input_names || [] if names.empty?
|
|
95
397
|
|
|
96
398
|
@input_names = names.flatten.map(&:to_sym)
|
|
97
399
|
end
|
|
98
400
|
|
|
99
|
-
|
|
401
|
+
# Registers a handler for exceptions raised by the chat operations of
|
|
402
|
+
# this agent's instances: #ask, #say, #ask_later, #complete,
|
|
403
|
+
# #generate, #run_tools, #step, #count_tokens, and #compact. Name the
|
|
404
|
+
# handler with +with:+ or pass a block; either runs on the agent instance,
|
|
405
|
+
# so the agent's #chat, inputs, and class name are available for instrumentation.
|
|
406
|
+
#
|
|
407
|
+
# class ApplicationAgent < RubyLLM::Agent
|
|
408
|
+
# rescue_from RubyLLM::RateLimitError, Faraday::TimeoutError, with: :handle_transient
|
|
409
|
+
# rescue_from RubyLLM::BadRequestError do |error|
|
|
410
|
+
# error_tracker.notify(error)
|
|
411
|
+
# raise
|
|
412
|
+
# end
|
|
413
|
+
#
|
|
414
|
+
# private
|
|
415
|
+
#
|
|
416
|
+
# def handle_transient(error)
|
|
417
|
+
# metrics.increment("llm.api_error", type: "transient")
|
|
418
|
+
# raise
|
|
419
|
+
# end
|
|
420
|
+
# end
|
|
421
|
+
#
|
|
422
|
+
# Handlers are searched in reverse declaration order, so the last
|
|
423
|
+
# matching one wins. Re-raise inside a handler to let the caller see
|
|
424
|
+
# the exception; otherwise the handler's return value becomes the
|
|
425
|
+
# operation's return value. Exceptions no handler matches are
|
|
426
|
+
# re-raised. Subclasses inherit the handlers declared when they are
|
|
427
|
+
# defined.
|
|
428
|
+
#
|
|
429
|
+
# Exception classes may be named as Strings, which defers constant
|
|
430
|
+
# lookup until an exception is raised.
|
|
431
|
+
def rescue_from(*exception_classes, with: nil, &block)
|
|
432
|
+
raise ArgumentError, 'rescue_from needs a handler: pass with: or a block' unless with || block
|
|
433
|
+
raise ArgumentError, 'rescue_from takes with: or a block, not both' if with && block
|
|
434
|
+
|
|
435
|
+
exception_classes.flatten.each do |exception_class|
|
|
436
|
+
rescue_handlers << [rescue_handler_key(exception_class), with || block]
|
|
437
|
+
end
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
def rescue_handlers # :nodoc:
|
|
441
|
+
@rescue_handlers ||= []
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
def rescue_handler_for(exception) # :nodoc:
|
|
445
|
+
rescue_handlers.reverse_each do |class_name, handler|
|
|
446
|
+
exception_class = rescue_handler_class(class_name)
|
|
447
|
+
return handler if exception_class && exception.is_a?(exception_class)
|
|
448
|
+
end
|
|
449
|
+
nil
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
def chat_kwargs # :nodoc:
|
|
100
453
|
@chat_kwargs || {}
|
|
101
454
|
end
|
|
102
455
|
|
|
456
|
+
def resolved_chat_kwargs(inputs: {}) # :nodoc:
|
|
457
|
+
kwargs = chat_kwargs
|
|
458
|
+
return kwargs unless kwargs[:model].is_a?(Proc)
|
|
459
|
+
|
|
460
|
+
kwargs.merge(model: evaluate(kwargs[:model], runtime_context(chat: nil, inputs: inputs)))
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
def build_chat(inputs:, options:) # :nodoc:
|
|
464
|
+
(context || RubyLLM).chat(**resolved_chat_kwargs(inputs:), **options)
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
# Builds a Chat configured with this agent's declarations and returns
|
|
468
|
+
# it. Keywords matching declared ::inputs become runtime inputs; the
|
|
469
|
+
# rest are forwarded to RubyLLM.chat.
|
|
470
|
+
#
|
|
471
|
+
# chat = WorkAssistant.chat
|
|
472
|
+
# chat.ask "Hello"
|
|
473
|
+
#
|
|
103
474
|
def chat(**kwargs)
|
|
104
475
|
input_values, chat_options = partition_inputs(kwargs)
|
|
105
|
-
chat =
|
|
476
|
+
chat = build_chat(inputs: input_values, options: chat_options)
|
|
106
477
|
apply_configuration(chat, input_values:, persist_instructions: true)
|
|
107
478
|
chat
|
|
108
479
|
end
|
|
109
480
|
|
|
481
|
+
# Creates a ::chat_model record, applies this agent's configuration to
|
|
482
|
+
# it, and returns it. Keywords matching declared ::inputs become
|
|
483
|
+
# runtime inputs; the rest are forwarded to the model's +create+.
|
|
484
|
+
#
|
|
485
|
+
# chat = WorkAssistant.create(user: current_user)
|
|
486
|
+
#
|
|
487
|
+
# Raises ArgumentError if ::chat_model is not configured.
|
|
110
488
|
def create(**kwargs)
|
|
111
489
|
with_rails_chat_record(:create, **kwargs)
|
|
112
490
|
end
|
|
113
491
|
|
|
492
|
+
# Like ::create, but calls the model's <tt>create!</tt>, raising if
|
|
493
|
+
# the record is invalid.
|
|
494
|
+
#
|
|
495
|
+
# chat = WorkAssistant.create!(user: current_user)
|
|
496
|
+
#
|
|
114
497
|
def create!(**kwargs)
|
|
115
498
|
with_rails_chat_record(:create!, **kwargs)
|
|
116
499
|
end
|
|
117
500
|
|
|
501
|
+
# Finds the ::chat_model record with +id+ and applies this agent's
|
|
502
|
+
# configuration at runtime, without persisting instructions. Returns
|
|
503
|
+
# the record.
|
|
504
|
+
#
|
|
505
|
+
# chat = WorkAssistant.find(params[:id])
|
|
506
|
+
#
|
|
507
|
+
# Raises ArgumentError if ::chat_model is not configured.
|
|
118
508
|
def find(id, **kwargs)
|
|
119
509
|
raise ArgumentError, 'chat_model must be configured to use find' unless resolved_chat_model
|
|
120
510
|
|
|
@@ -125,112 +515,225 @@ module RubyLLM
|
|
|
125
515
|
record
|
|
126
516
|
end
|
|
127
517
|
|
|
128
|
-
|
|
129
|
-
|
|
518
|
+
# Re-renders this agent's instructions and persists them on the given
|
|
519
|
+
# ::chat_model record (or the record found by that id). Keywords
|
|
520
|
+
# matching declared ::inputs become runtime inputs. Returns the
|
|
521
|
+
# record.
|
|
522
|
+
#
|
|
523
|
+
# WorkAssistant.sync_instructions(chat)
|
|
524
|
+
#
|
|
525
|
+
# Raises ArgumentError if ::chat_model is not configured.
|
|
526
|
+
def sync_instructions(chat_or_id, **kwargs)
|
|
527
|
+
raise ArgumentError, 'chat_model must be configured to use sync_instructions' unless resolved_chat_model
|
|
130
528
|
|
|
131
529
|
input_values, = partition_inputs(kwargs)
|
|
132
530
|
record = chat_or_id.is_a?(resolved_chat_model) ? chat_or_id : resolved_chat_model.find(chat_or_id)
|
|
133
531
|
apply_assume_model_exists(record)
|
|
532
|
+
apply_protocol(record)
|
|
533
|
+
apply_context(record)
|
|
134
534
|
runtime = runtime_context(chat: record, inputs: input_values)
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
535
|
+
apply_instructions(
|
|
536
|
+
record,
|
|
537
|
+
runtime,
|
|
538
|
+
inputs: input_values,
|
|
539
|
+
persist: true,
|
|
540
|
+
persistent_only: true
|
|
541
|
+
)
|
|
139
542
|
record
|
|
140
543
|
end
|
|
141
544
|
|
|
142
|
-
def render_prompt(name, chat:, inputs:, locals:)
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
545
|
+
def render_prompt(name, chat:, inputs:, locals:) # :nodoc:
|
|
546
|
+
resolved_locals = resolve_prompt_locals(locals, runtime: runtime_context(chat:, inputs:), chat:, inputs:)
|
|
547
|
+
RubyLLM.render_prompt("#{prompt_agent_path}/#{name}", **resolved_locals)
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
def partition_inputs(kwargs) # :nodoc:
|
|
551
|
+
input_values = {}
|
|
552
|
+
chat_options = {}
|
|
553
|
+
|
|
554
|
+
kwargs.each do |key, value|
|
|
555
|
+
symbolized_key = key.to_sym
|
|
556
|
+
if inputs.include?(symbolized_key)
|
|
557
|
+
input_values[symbolized_key] = value
|
|
558
|
+
else
|
|
559
|
+
chat_options[symbolized_key] = value
|
|
560
|
+
end
|
|
147
561
|
end
|
|
148
562
|
|
|
149
|
-
|
|
150
|
-
|
|
563
|
+
[input_values, chat_options]
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
def apply_configuration(chat, input_values:, persist_instructions:) # :nodoc:
|
|
567
|
+
runtime = runtime_context(chat:, inputs: input_values)
|
|
568
|
+
apply_chat_options(chat)
|
|
569
|
+
apply_context(chat)
|
|
570
|
+
apply_instructions(chat, runtime, inputs: input_values, persist: persist_instructions)
|
|
571
|
+
apply_tools(chat, runtime)
|
|
572
|
+
apply_passthrough_options(chat)
|
|
573
|
+
apply_thinking(chat)
|
|
574
|
+
apply_citations(chat)
|
|
575
|
+
apply_end_user(chat, runtime)
|
|
576
|
+
apply_caching(chat, runtime)
|
|
577
|
+
apply_compaction(chat)
|
|
578
|
+
apply_provider_options(chat, runtime)
|
|
579
|
+
apply_headers(chat, runtime)
|
|
580
|
+
apply_schema(chat, runtime)
|
|
581
|
+
apply_fallbacks(chat)
|
|
151
582
|
end
|
|
152
583
|
|
|
153
584
|
private
|
|
154
585
|
|
|
586
|
+
def validate_thinking_options(enabled, options)
|
|
587
|
+
raise ArgumentError, 'thinking accepts false or thinking options' unless [true, false].include?(enabled)
|
|
588
|
+
raise ArgumentError, 'thinking false does not accept options' if !enabled && options.any?
|
|
589
|
+
raise ArgumentError, 'thinking options cannot be nil; use thinking false to disable' if options.value?(nil)
|
|
590
|
+
|
|
591
|
+
unsupported = options.keys - THINKING_OPTIONS
|
|
592
|
+
return if unsupported.empty?
|
|
593
|
+
|
|
594
|
+
raise ArgumentError, "thinking accepts #{THINKING_OPTIONS.join(', ')}, got #{unsupported.join(', ')}"
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
def rescue_handler_key(exception_class)
|
|
598
|
+
case exception_class
|
|
599
|
+
when Module then exception_class.name || exception_class
|
|
600
|
+
when String then exception_class
|
|
601
|
+
else raise ArgumentError, "#{exception_class.inspect} is not an exception class or its name"
|
|
602
|
+
end
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
def rescue_handler_class(class_name)
|
|
606
|
+
return class_name if class_name.is_a?(Module)
|
|
607
|
+
|
|
608
|
+
Object.const_get(class_name)
|
|
609
|
+
rescue NameError
|
|
610
|
+
nil
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
def copy_inherited_config_to(subclass)
|
|
614
|
+
DUPED_INHERITED_CONFIG.each do |ivar, default|
|
|
615
|
+
value = instance_variable_defined?(ivar) ? instance_variable_get(ivar) : default
|
|
616
|
+
subclass.instance_variable_set(ivar, value.respond_to?(:dup) ? value.dup : value)
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
COPIED_INHERITED_CONFIG.each do |ivar|
|
|
620
|
+
subclass.instance_variable_set(ivar, instance_variable_get(ivar))
|
|
621
|
+
end
|
|
622
|
+
end
|
|
623
|
+
|
|
155
624
|
def with_rails_chat_record(method_name, **kwargs)
|
|
156
625
|
raise ArgumentError, 'chat_model must be configured to use create/create!' unless resolved_chat_model
|
|
157
626
|
|
|
158
627
|
input_values, chat_options = partition_inputs(kwargs)
|
|
159
|
-
record = resolved_chat_model.public_send(
|
|
628
|
+
record = resolved_chat_model.public_send(
|
|
629
|
+
method_name, **resolved_chat_kwargs(inputs: input_values), **chat_options
|
|
630
|
+
)
|
|
160
631
|
apply_configuration(record, input_values:, persist_instructions: true) if record
|
|
161
632
|
record
|
|
162
633
|
end
|
|
163
634
|
|
|
164
|
-
def
|
|
165
|
-
|
|
166
|
-
|
|
635
|
+
def apply_context(chat)
|
|
636
|
+
chat.with_context(context) if context
|
|
637
|
+
end
|
|
638
|
+
|
|
639
|
+
def apply_instructions(chat, runtime, inputs:, persist:, persistent_only: false)
|
|
640
|
+
instructions_config.each do |declaration|
|
|
641
|
+
next if persistent_only && !declaration[:persist]
|
|
167
642
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
643
|
+
value = resolved_instruction_value(declaration, chat, runtime, inputs:)
|
|
644
|
+
next if blank_instruction?(value)
|
|
645
|
+
|
|
646
|
+
options = {
|
|
647
|
+
append: declaration[:append],
|
|
648
|
+
cache_until_here: declaration[:cache_until_here]
|
|
649
|
+
}
|
|
650
|
+
options[:persist] = persist && declaration[:persist] if rails_chat_record?(chat)
|
|
651
|
+
chat.with_instructions(value, **options)
|
|
652
|
+
end
|
|
176
653
|
end
|
|
177
654
|
|
|
178
|
-
|
|
179
|
-
|
|
655
|
+
# An empty prompt file, such as the one the agent generator writes,
|
|
656
|
+
# means no instructions rather than an empty system message.
|
|
657
|
+
def blank_instruction?(value)
|
|
658
|
+
value.nil? || (value.respond_to?(:strip) && value.strip.empty?)
|
|
180
659
|
end
|
|
181
660
|
|
|
182
|
-
def
|
|
183
|
-
|
|
184
|
-
|
|
661
|
+
def apply_tools(chat, runtime)
|
|
662
|
+
tools_to_apply = Array(evaluate(tools, runtime)).compact
|
|
663
|
+
chat.with_tools(*tools_to_apply) if tools_to_apply.any?
|
|
185
664
|
|
|
186
|
-
|
|
187
|
-
|
|
665
|
+
options = evaluate(tool_options, runtime)
|
|
666
|
+
chat.with_tool_options(**options) if options && !options.empty?
|
|
188
667
|
|
|
189
|
-
|
|
668
|
+
server_tools_to_apply = Array(evaluate(server_tools, runtime)).compact
|
|
669
|
+
chat.with_server_tools(*server_tools_to_apply) if server_tools_to_apply.any?
|
|
190
670
|
end
|
|
191
671
|
|
|
192
|
-
def
|
|
193
|
-
|
|
194
|
-
|
|
672
|
+
def apply_passthrough_options(chat)
|
|
673
|
+
PASSTHROUGH_OPTIONS.each do |option|
|
|
674
|
+
value = instance_variable_get(:"@#{option}")
|
|
675
|
+
chat.public_send(:"with_#{option}", value) unless value.nil?
|
|
676
|
+
end
|
|
195
677
|
end
|
|
196
678
|
|
|
197
|
-
def
|
|
198
|
-
|
|
679
|
+
def apply_thinking(chat)
|
|
680
|
+
return if @thinking.nil?
|
|
681
|
+
|
|
682
|
+
@thinking == false ? chat.with_thinking(false) : chat.with_thinking(**@thinking)
|
|
199
683
|
end
|
|
200
684
|
|
|
201
|
-
def
|
|
202
|
-
|
|
685
|
+
def apply_citations(chat)
|
|
686
|
+
chat.with_citations(@citations) unless @citations.nil?
|
|
203
687
|
end
|
|
204
688
|
|
|
205
|
-
def
|
|
206
|
-
value = evaluate(
|
|
207
|
-
|
|
689
|
+
def apply_end_user(chat, runtime)
|
|
690
|
+
value = evaluate(end_user, runtime)
|
|
691
|
+
chat.with_end_user(value) unless value.nil?
|
|
208
692
|
end
|
|
209
693
|
|
|
210
|
-
def
|
|
694
|
+
def apply_caching(chat, runtime)
|
|
695
|
+
value = evaluate(@caching, runtime)
|
|
696
|
+
return if value.nil?
|
|
697
|
+
|
|
698
|
+
value == false ? chat.with_caching(false) : chat.with_caching(**value)
|
|
699
|
+
end
|
|
700
|
+
|
|
701
|
+
def apply_compaction(chat)
|
|
702
|
+
return if @compaction.nil?
|
|
703
|
+
|
|
704
|
+
@compaction == false ? chat.with_compaction(false) : chat.with_compaction(**@compaction)
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
def apply_provider_options(chat, runtime)
|
|
708
|
+
value = evaluate(provider_options, runtime)
|
|
709
|
+
chat.with_provider_options(**value) if value && !value.empty?
|
|
710
|
+
end
|
|
711
|
+
|
|
712
|
+
def apply_headers(chat, runtime)
|
|
211
713
|
value = evaluate(headers, runtime)
|
|
212
|
-
|
|
714
|
+
chat.with_headers(**value) if value && !value.empty?
|
|
213
715
|
end
|
|
214
716
|
|
|
215
|
-
def apply_schema(
|
|
717
|
+
def apply_schema(chat, runtime)
|
|
216
718
|
value = resolved_schema_value(runtime)
|
|
217
|
-
|
|
719
|
+
chat.with_schema(value) if value
|
|
720
|
+
end
|
|
721
|
+
|
|
722
|
+
def apply_fallbacks(chat)
|
|
723
|
+
chat.with_fallbacks(*fallbacks, **fallback_options) if fallbacks.any?
|
|
218
724
|
end
|
|
219
725
|
|
|
220
726
|
def resolved_schema_value(runtime)
|
|
221
727
|
value = schema
|
|
222
728
|
return value unless value.is_a?(Proc)
|
|
729
|
+
return evaluate(value, runtime) if value.lambda?
|
|
223
730
|
|
|
224
|
-
|
|
225
|
-
rescue NoMethodError => e
|
|
226
|
-
raise unless e.receiver.equal?(runtime)
|
|
227
|
-
|
|
228
|
-
RubyLLM::Schema.create(&value)
|
|
731
|
+
Schematist::Schema.create(&value)
|
|
229
732
|
end
|
|
230
733
|
|
|
231
|
-
def
|
|
232
|
-
apply_assume_model_exists(
|
|
233
|
-
|
|
734
|
+
def apply_chat_options(chat)
|
|
735
|
+
apply_assume_model_exists(chat)
|
|
736
|
+
apply_protocol(chat)
|
|
234
737
|
end
|
|
235
738
|
|
|
236
739
|
def apply_assume_model_exists(chat_object)
|
|
@@ -241,12 +744,20 @@ module RubyLLM
|
|
|
241
744
|
chat_object.assume_model_exists = chat_kwargs[:assume_model_exists]
|
|
242
745
|
end
|
|
243
746
|
|
|
747
|
+
def apply_protocol(chat_object)
|
|
748
|
+
return unless chat_kwargs.key?(:protocol) &&
|
|
749
|
+
resolved_chat_model &&
|
|
750
|
+
chat_object.is_a?(resolved_chat_model)
|
|
751
|
+
|
|
752
|
+
chat_object.protocol = chat_kwargs[:protocol]
|
|
753
|
+
end
|
|
754
|
+
|
|
244
755
|
def evaluate(value, runtime)
|
|
245
756
|
value.is_a?(Proc) ? runtime.instance_exec(&value) : value
|
|
246
757
|
end
|
|
247
758
|
|
|
248
|
-
def
|
|
249
|
-
value = evaluate(
|
|
759
|
+
def resolved_instruction_value(declaration, chat_object, runtime, inputs:)
|
|
760
|
+
value = evaluate(declaration[:value], runtime)
|
|
250
761
|
return value unless prompt_instruction?(value)
|
|
251
762
|
|
|
252
763
|
runtime.prompt(
|
|
@@ -255,26 +766,32 @@ module RubyLLM
|
|
|
255
766
|
)
|
|
256
767
|
end
|
|
257
768
|
|
|
258
|
-
def
|
|
259
|
-
|
|
769
|
+
def instructions_config
|
|
770
|
+
return instruction_declarations if instruction_declarations.any?
|
|
771
|
+
return [] unless default_instructions_prompt_exists?
|
|
772
|
+
|
|
773
|
+
[{
|
|
774
|
+
value: { prompt: 'instructions', locals: {} },
|
|
775
|
+
append: false,
|
|
776
|
+
persist: true,
|
|
777
|
+
cache_until_here: false
|
|
778
|
+
}]
|
|
260
779
|
end
|
|
261
780
|
|
|
262
|
-
def
|
|
263
|
-
|
|
264
|
-
chat_object
|
|
265
|
-
else
|
|
266
|
-
runtime_instruction_target(chat_object)
|
|
267
|
-
end
|
|
781
|
+
def instruction_declarations
|
|
782
|
+
@instruction_declarations ||= []
|
|
268
783
|
end
|
|
269
784
|
|
|
270
|
-
def
|
|
271
|
-
|
|
785
|
+
def rails_chat_record?(chat)
|
|
786
|
+
resolved_chat_model && chat.is_a?(resolved_chat_model)
|
|
787
|
+
end
|
|
272
788
|
|
|
273
|
-
|
|
789
|
+
def default_instructions_prompt_exists?
|
|
790
|
+
name && File.exist?(Prompt.new("#{prompt_agent_path}/instructions").path)
|
|
274
791
|
end
|
|
275
792
|
|
|
276
|
-
def
|
|
277
|
-
|
|
793
|
+
def prompt_instruction?(value)
|
|
794
|
+
value.is_a?(Hash) && value[:prompt]
|
|
278
795
|
end
|
|
279
796
|
|
|
280
797
|
def resolve_prompt_locals(locals, runtime:, chat:, inputs:)
|
|
@@ -285,22 +802,6 @@ module RubyLLM
|
|
|
285
802
|
base.merge(evaluated)
|
|
286
803
|
end
|
|
287
804
|
|
|
288
|
-
def partition_inputs(kwargs)
|
|
289
|
-
input_values = {}
|
|
290
|
-
chat_options = {}
|
|
291
|
-
|
|
292
|
-
kwargs.each do |key, value|
|
|
293
|
-
symbolized_key = key.to_sym
|
|
294
|
-
if inputs.include?(symbolized_key)
|
|
295
|
-
input_values[symbolized_key] = value
|
|
296
|
-
else
|
|
297
|
-
chat_options[symbolized_key] = value
|
|
298
|
-
end
|
|
299
|
-
end
|
|
300
|
-
|
|
301
|
-
[input_values, chat_options]
|
|
302
|
-
end
|
|
303
|
-
|
|
304
805
|
def runtime_context(chat:, inputs:)
|
|
305
806
|
agent_class = self
|
|
306
807
|
Object.new.tap do |runtime|
|
|
@@ -315,27 +816,9 @@ module RubyLLM
|
|
|
315
816
|
end
|
|
316
817
|
end
|
|
317
818
|
|
|
318
|
-
def prompt_path_for(name)
|
|
319
|
-
filename = name.to_s
|
|
320
|
-
filename += '.txt.erb' unless filename.end_with?('.txt.erb')
|
|
321
|
-
prompt_root.join(prompt_agent_path, filename)
|
|
322
|
-
end
|
|
323
|
-
|
|
324
819
|
def prompt_agent_path
|
|
325
820
|
class_name = name || 'agent'
|
|
326
|
-
class_name.gsub('::', '/')
|
|
327
|
-
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
328
|
-
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
329
|
-
.tr('-', '_')
|
|
330
|
-
.downcase
|
|
331
|
-
end
|
|
332
|
-
|
|
333
|
-
def prompt_root
|
|
334
|
-
if defined?(Rails) && Rails.respond_to?(:root) && Rails.root
|
|
335
|
-
Rails.root.join('app/prompts')
|
|
336
|
-
else
|
|
337
|
-
Pathname.new(Dir.pwd).join('app/prompts')
|
|
338
|
-
end
|
|
821
|
+
Support::Utils.underscore(class_name.gsub('::', '/')).tr('-', '_')
|
|
339
822
|
end
|
|
340
823
|
|
|
341
824
|
def resolved_chat_model
|
|
@@ -348,19 +831,451 @@ module RubyLLM
|
|
|
348
831
|
end
|
|
349
832
|
end
|
|
350
833
|
|
|
834
|
+
# Returns a new agent wrapping +chat:+, or wrapping a newly built chat
|
|
835
|
+
# when +chat:+ is +nil+. Applies the agent's configuration either way.
|
|
836
|
+
# Keywords matching declared inputs (and the +inputs:+ hash) become
|
|
837
|
+
# runtime inputs; the rest are forwarded to RubyLLM.chat when the agent
|
|
838
|
+
# builds its own chat. Pass <tt>persist_instructions: false</tt> to
|
|
839
|
+
# apply instructions at runtime only, without persisting them on a
|
|
840
|
+
# Rails-backed record.
|
|
841
|
+
#
|
|
842
|
+
# agent = WorkAssistant.new
|
|
843
|
+
# agent.ask "Hello"
|
|
844
|
+
#
|
|
845
|
+
# record = Chat.find(params[:id])
|
|
846
|
+
# WorkAssistant.new(chat: record)
|
|
847
|
+
#
|
|
351
848
|
def initialize(chat: nil, inputs: nil, persist_instructions: true, **kwargs)
|
|
352
|
-
input_values, chat_options = self.class.
|
|
353
|
-
|
|
354
|
-
self.class.
|
|
355
|
-
|
|
849
|
+
input_values, chat_options = self.class.partition_inputs(kwargs)
|
|
850
|
+
input_values = input_values.merge(inputs || {})
|
|
851
|
+
@chat = chat || self.class.build_chat(inputs: input_values, options: chat_options)
|
|
852
|
+
self.class.apply_configuration(@chat, input_values:, persist_instructions:)
|
|
356
853
|
end
|
|
357
854
|
|
|
855
|
+
# The wrapped Chat, or the chat record in Rails mode.
|
|
358
856
|
attr_reader :chat
|
|
359
857
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
858
|
+
# Agent instances delegate the Chat conversation API to the wrapped
|
|
859
|
+
# #chat without adapting its return values.
|
|
860
|
+
def_delegators :chat, *CHAINABLE_CHAT_DELEGATES, *PASSTHROUGH_CHAT_DELEGATES
|
|
861
|
+
|
|
862
|
+
##
|
|
863
|
+
# :method: with_instructions
|
|
864
|
+
# :call-seq: with_instructions(instructions, append: false, cache_until_here: false)
|
|
865
|
+
#
|
|
866
|
+
# Delegates to Chat#with_instructions. See that method for arguments and return values.
|
|
867
|
+
|
|
868
|
+
##
|
|
869
|
+
# :method: with_tools
|
|
870
|
+
# :call-seq: with_tools(*tools)
|
|
871
|
+
#
|
|
872
|
+
# Delegates to Chat#with_tools. See that method for arguments and return values.
|
|
873
|
+
|
|
874
|
+
##
|
|
875
|
+
# :method: with_server_tools
|
|
876
|
+
# :call-seq: with_server_tools(*tools, **tools_with_options)
|
|
877
|
+
#
|
|
878
|
+
# Delegates to Chat#with_server_tools. See that method for arguments and return values.
|
|
879
|
+
|
|
880
|
+
##
|
|
881
|
+
# :method: with_tool_options
|
|
882
|
+
# :call-seq: with_tool_options(**options)
|
|
883
|
+
#
|
|
884
|
+
# Delegates to Chat#with_tool_options. See that method for arguments and return values.
|
|
885
|
+
|
|
886
|
+
##
|
|
887
|
+
# :method: with_model
|
|
888
|
+
# :call-seq: with_model(model_id, provider: nil, protocol: nil, assume_model_exists: false)
|
|
889
|
+
#
|
|
890
|
+
# Delegates to Chat#with_model. See that method for arguments and return values.
|
|
891
|
+
|
|
892
|
+
##
|
|
893
|
+
# :method: with_temperature
|
|
894
|
+
# :call-seq: with_temperature(temperature)
|
|
895
|
+
#
|
|
896
|
+
# Delegates to Chat#with_temperature. See that method for arguments and return values.
|
|
897
|
+
|
|
898
|
+
##
|
|
899
|
+
# :method: with_max_output_tokens
|
|
900
|
+
# :call-seq: with_max_output_tokens(max_output_tokens)
|
|
901
|
+
#
|
|
902
|
+
# Delegates to Chat#with_max_output_tokens. See that method for arguments and return values.
|
|
903
|
+
|
|
904
|
+
##
|
|
905
|
+
# :method: with_thinking
|
|
906
|
+
# :call-seq: with_thinking(enabled = true, **options)
|
|
907
|
+
#
|
|
908
|
+
# Delegates to Chat#with_thinking. See that method for arguments and return values.
|
|
909
|
+
|
|
910
|
+
##
|
|
911
|
+
# :method: with_citations
|
|
912
|
+
# :call-seq: with_citations(enabled = true)
|
|
913
|
+
#
|
|
914
|
+
# Delegates to Chat#with_citations. See that method for arguments and return values.
|
|
915
|
+
|
|
916
|
+
##
|
|
917
|
+
# :method: with_end_user
|
|
918
|
+
# :call-seq: with_end_user(end_user)
|
|
919
|
+
#
|
|
920
|
+
# Delegates to Chat#with_end_user. See that method for arguments and return values.
|
|
921
|
+
|
|
922
|
+
##
|
|
923
|
+
# :method: with_compaction
|
|
924
|
+
# :call-seq: with_compaction(options = {})
|
|
925
|
+
#
|
|
926
|
+
# Delegates to Chat#with_compaction. See that method for arguments and return values.
|
|
927
|
+
|
|
928
|
+
##
|
|
929
|
+
# :method: with_caching
|
|
930
|
+
# :call-seq: with_caching(options = {})
|
|
931
|
+
#
|
|
932
|
+
# Delegates to Chat#with_caching. See that method for arguments and return values.
|
|
933
|
+
|
|
934
|
+
##
|
|
935
|
+
# :method: with_context
|
|
936
|
+
# :call-seq: with_context(context)
|
|
937
|
+
#
|
|
938
|
+
# Delegates to Chat#with_context. See that method for arguments and return values.
|
|
939
|
+
|
|
940
|
+
##
|
|
941
|
+
# :method: with_provider_options
|
|
942
|
+
# :call-seq: with_provider_options(provider_options)
|
|
943
|
+
#
|
|
944
|
+
# Delegates to Chat#with_provider_options. See that method for arguments and return values.
|
|
945
|
+
|
|
946
|
+
##
|
|
947
|
+
# :method: with_headers
|
|
948
|
+
# :call-seq: with_headers(headers)
|
|
949
|
+
#
|
|
950
|
+
# Delegates to Chat#with_headers. See that method for arguments and return values.
|
|
951
|
+
|
|
952
|
+
##
|
|
953
|
+
# :method: with_schema
|
|
954
|
+
# :call-seq: with_schema(schema)
|
|
955
|
+
#
|
|
956
|
+
# Delegates to Chat#with_schema. See that method for arguments and return values.
|
|
957
|
+
|
|
958
|
+
##
|
|
959
|
+
# :method: with_fallbacks
|
|
960
|
+
# :call-seq: with_fallbacks(*models, on: Fallback::DEFAULT_ERRORS)
|
|
961
|
+
#
|
|
962
|
+
# Delegates to Chat#with_fallbacks. See that method for arguments and return values.
|
|
963
|
+
|
|
964
|
+
##
|
|
965
|
+
# :method: before_request
|
|
966
|
+
# :call-seq: before_request(&block)
|
|
967
|
+
#
|
|
968
|
+
# Delegates to Chat#before_request. See that method for arguments and return values.
|
|
969
|
+
|
|
970
|
+
##
|
|
971
|
+
# :method: before_message
|
|
972
|
+
# :call-seq: before_message(&block)
|
|
973
|
+
#
|
|
974
|
+
# Delegates to Chat#before_message. See that method for arguments and return values.
|
|
975
|
+
|
|
976
|
+
##
|
|
977
|
+
# :method: after_message
|
|
978
|
+
# :call-seq: after_message(&block)
|
|
979
|
+
#
|
|
980
|
+
# Delegates to Chat#after_message. See that method for arguments and return values.
|
|
981
|
+
|
|
982
|
+
##
|
|
983
|
+
# :method: before_tool_call
|
|
984
|
+
# :call-seq: before_tool_call(&block)
|
|
985
|
+
#
|
|
986
|
+
# Delegates to Chat#before_tool_call. See that method for arguments and return values.
|
|
987
|
+
|
|
988
|
+
##
|
|
989
|
+
# :method: after_tool_result
|
|
990
|
+
# :call-seq: after_tool_result(&block)
|
|
991
|
+
#
|
|
992
|
+
# Delegates to Chat#after_tool_result. See that method for arguments and return values.
|
|
993
|
+
|
|
994
|
+
##
|
|
995
|
+
# :method: before_fallback
|
|
996
|
+
# :call-seq: before_fallback(&block)
|
|
997
|
+
#
|
|
998
|
+
# Delegates to Chat#before_fallback. See that method for arguments and return values.
|
|
999
|
+
|
|
1000
|
+
##
|
|
1001
|
+
# :method: after_fallback
|
|
1002
|
+
# :call-seq: after_fallback(&block)
|
|
1003
|
+
#
|
|
1004
|
+
# Delegates to Chat#after_fallback. See that method for arguments and return values.
|
|
1005
|
+
|
|
1006
|
+
##
|
|
1007
|
+
# :method: cancel
|
|
1008
|
+
# :call-seq: cancel()
|
|
1009
|
+
#
|
|
1010
|
+
# Delegates to Chat#cancel. See that method for arguments and return values.
|
|
1011
|
+
|
|
1012
|
+
##
|
|
1013
|
+
# :method: approve
|
|
1014
|
+
# :call-seq: approve(tool_call)
|
|
1015
|
+
#
|
|
1016
|
+
# Delegates to Chat#approve. See that method for arguments and return values.
|
|
1017
|
+
|
|
1018
|
+
##
|
|
1019
|
+
# :method: deny
|
|
1020
|
+
# :call-seq: deny(tool_call)
|
|
1021
|
+
#
|
|
1022
|
+
# Delegates to Chat#deny. See that method for arguments and return values.
|
|
1023
|
+
|
|
1024
|
+
##
|
|
1025
|
+
# :method: cache_until_here
|
|
1026
|
+
# :call-seq: cache_until_here()
|
|
1027
|
+
#
|
|
1028
|
+
# Delegates to Chat#cache_until_here. See that method for arguments and return values.
|
|
1029
|
+
|
|
1030
|
+
##
|
|
1031
|
+
# :method: model
|
|
1032
|
+
# :call-seq: model
|
|
1033
|
+
#
|
|
1034
|
+
# Delegates to Chat#model. See that method for arguments and return values.
|
|
1035
|
+
|
|
1036
|
+
##
|
|
1037
|
+
# :method: provider
|
|
1038
|
+
# :call-seq: provider
|
|
1039
|
+
#
|
|
1040
|
+
# Delegates to Chat#provider. See that method for arguments and return values.
|
|
1041
|
+
|
|
1042
|
+
##
|
|
1043
|
+
# :method: messages
|
|
1044
|
+
# :call-seq: messages
|
|
1045
|
+
#
|
|
1046
|
+
# Delegates to Chat#messages. See that method for arguments and return values.
|
|
1047
|
+
|
|
1048
|
+
##
|
|
1049
|
+
# :method: tools
|
|
1050
|
+
# :call-seq: tools
|
|
1051
|
+
#
|
|
1052
|
+
# Delegates to Chat#tools. See that method for arguments and return values.
|
|
1053
|
+
|
|
1054
|
+
##
|
|
1055
|
+
# :method: server_tools
|
|
1056
|
+
# :call-seq: server_tools
|
|
1057
|
+
#
|
|
1058
|
+
# Delegates to Chat#server_tools. See that method for arguments and return values.
|
|
1059
|
+
|
|
1060
|
+
##
|
|
1061
|
+
# :method: tool_options
|
|
1062
|
+
# :call-seq: tool_options()
|
|
1063
|
+
#
|
|
1064
|
+
# Delegates to Chat#tool_options. See that method for arguments and return values.
|
|
1065
|
+
|
|
1066
|
+
##
|
|
1067
|
+
# :method: provider_options
|
|
1068
|
+
# :call-seq: provider_options
|
|
1069
|
+
#
|
|
1070
|
+
# Delegates to Chat#provider_options. See that method for arguments and return values.
|
|
1071
|
+
|
|
1072
|
+
##
|
|
1073
|
+
# :method: headers
|
|
1074
|
+
# :call-seq: headers
|
|
1075
|
+
#
|
|
1076
|
+
# Delegates to Chat#headers. See that method for arguments and return values.
|
|
1077
|
+
|
|
1078
|
+
##
|
|
1079
|
+
# :method: schema
|
|
1080
|
+
# :call-seq: schema
|
|
1081
|
+
#
|
|
1082
|
+
# Delegates to Chat#schema. See that method for arguments and return values.
|
|
1083
|
+
|
|
1084
|
+
##
|
|
1085
|
+
# :method: concurrency
|
|
1086
|
+
# :call-seq: concurrency
|
|
1087
|
+
#
|
|
1088
|
+
# Delegates to Chat#concurrency. See that method for arguments and return values.
|
|
1089
|
+
|
|
1090
|
+
##
|
|
1091
|
+
# :method: caching
|
|
1092
|
+
# :call-seq: caching
|
|
1093
|
+
#
|
|
1094
|
+
# Delegates to Chat#caching. See that method for arguments and return values.
|
|
1095
|
+
|
|
1096
|
+
##
|
|
1097
|
+
# :method: citations
|
|
1098
|
+
# :call-seq: citations
|
|
1099
|
+
#
|
|
1100
|
+
# Delegates to Chat#citations. See that method for arguments and return values.
|
|
1101
|
+
|
|
1102
|
+
##
|
|
1103
|
+
# :method: compaction
|
|
1104
|
+
# :call-seq: compaction
|
|
1105
|
+
#
|
|
1106
|
+
# Delegates to Chat#compaction. See that method for arguments and return values.
|
|
1107
|
+
|
|
1108
|
+
##
|
|
1109
|
+
# :method: context
|
|
1110
|
+
# :call-seq: context
|
|
1111
|
+
#
|
|
1112
|
+
# Delegates to Chat#context. See that method for arguments and return values.
|
|
1113
|
+
|
|
1114
|
+
##
|
|
1115
|
+
# :method: end_user
|
|
1116
|
+
# :call-seq: end_user
|
|
1117
|
+
#
|
|
1118
|
+
# Delegates to Chat#end_user. See that method for arguments and return values.
|
|
1119
|
+
|
|
1120
|
+
##
|
|
1121
|
+
# :method: fallbacks
|
|
1122
|
+
# :call-seq: fallbacks
|
|
1123
|
+
#
|
|
1124
|
+
# Delegates to Chat#fallbacks. See that method for arguments and return values.
|
|
1125
|
+
|
|
1126
|
+
##
|
|
1127
|
+
# :method: thinking
|
|
1128
|
+
# :call-seq: thinking()
|
|
1129
|
+
#
|
|
1130
|
+
# Delegates to Chat#thinking. See that method for arguments and return values.
|
|
1131
|
+
|
|
1132
|
+
##
|
|
1133
|
+
# :method: temperature
|
|
1134
|
+
# :call-seq: temperature
|
|
1135
|
+
#
|
|
1136
|
+
# Returns Chat#temperature from the wrapped chat.
|
|
1137
|
+
|
|
1138
|
+
##
|
|
1139
|
+
# :method: max_output_tokens
|
|
1140
|
+
# :call-seq: max_output_tokens
|
|
1141
|
+
#
|
|
1142
|
+
# Returns Chat#max_output_tokens from the wrapped chat.
|
|
1143
|
+
|
|
1144
|
+
##
|
|
1145
|
+
# :method: each
|
|
1146
|
+
# :call-seq: each(&block)
|
|
1147
|
+
#
|
|
1148
|
+
# Delegates to Chat#each. See that method for arguments and return values.
|
|
1149
|
+
|
|
1150
|
+
##
|
|
1151
|
+
# :method: complete?
|
|
1152
|
+
# :call-seq: complete?()
|
|
1153
|
+
#
|
|
1154
|
+
# Delegates to Chat#complete?. See that method for arguments and return values.
|
|
1155
|
+
|
|
1156
|
+
##
|
|
1157
|
+
# :method: cancelled?
|
|
1158
|
+
# :call-seq: cancelled?()
|
|
1159
|
+
#
|
|
1160
|
+
# Delegates to Chat#cancelled?. See that method for arguments and return values.
|
|
1161
|
+
|
|
1162
|
+
##
|
|
1163
|
+
# :method: awaiting_approval?
|
|
1164
|
+
# :call-seq: awaiting_approval?()
|
|
1165
|
+
#
|
|
1166
|
+
# Delegates to Chat#awaiting_approval?. See that method for arguments and return values.
|
|
1167
|
+
|
|
1168
|
+
##
|
|
1169
|
+
# :method: pending_approvals
|
|
1170
|
+
# :call-seq: pending_approvals()
|
|
1171
|
+
#
|
|
1172
|
+
# Delegates to Chat#pending_approvals. See that method for arguments and return values.
|
|
1173
|
+
|
|
1174
|
+
##
|
|
1175
|
+
# :method: add_message
|
|
1176
|
+
# :call-seq: add_message(message_or_attributes)
|
|
1177
|
+
#
|
|
1178
|
+
# Delegates to Chat#add_message. See that method for arguments and return values.
|
|
1179
|
+
|
|
1180
|
+
##
|
|
1181
|
+
# :method: tokens
|
|
1182
|
+
# :call-seq: tokens()
|
|
1183
|
+
#
|
|
1184
|
+
# Delegates to Chat#tokens. See that method for arguments and return values.
|
|
1185
|
+
|
|
1186
|
+
##
|
|
1187
|
+
# :method: cost
|
|
1188
|
+
# :call-seq: cost()
|
|
1189
|
+
#
|
|
1190
|
+
# Delegates to Chat#cost. See that method for arguments and return values.
|
|
1191
|
+
|
|
1192
|
+
##
|
|
1193
|
+
# :method: render
|
|
1194
|
+
# :call-seq: render()
|
|
1195
|
+
#
|
|
1196
|
+
# Delegates to Chat#render. See that method for arguments and return values.
|
|
1197
|
+
|
|
1198
|
+
##
|
|
1199
|
+
# :method: ask
|
|
1200
|
+
# :call-seq: ask(message = nil, with: nil, &block)
|
|
1201
|
+
#
|
|
1202
|
+
# Delegates to Chat#ask, routing exceptions through the handlers
|
|
1203
|
+
# declared with ::rescue_from.
|
|
1204
|
+
|
|
1205
|
+
##
|
|
1206
|
+
# :method: complete
|
|
1207
|
+
# :call-seq: complete(&block)
|
|
1208
|
+
#
|
|
1209
|
+
# Delegates to Chat#complete, routing exceptions through the handlers
|
|
1210
|
+
# declared with ::rescue_from.
|
|
1211
|
+
|
|
1212
|
+
##
|
|
1213
|
+
# :method: say
|
|
1214
|
+
# :call-seq: say(message = nil, with: nil, &block)
|
|
1215
|
+
#
|
|
1216
|
+
# Delegates to Chat#say, the alias for Chat#ask, with ::rescue_from handling.
|
|
1217
|
+
|
|
1218
|
+
##
|
|
1219
|
+
# :method: ask_later
|
|
1220
|
+
# :call-seq: ask_later(message = nil, with: nil)
|
|
1221
|
+
#
|
|
1222
|
+
# Stages a message through Chat#ask_later without calling the provider.
|
|
1223
|
+
# Returns the wrapped chat. Exceptions use ::rescue_from handlers.
|
|
1224
|
+
|
|
1225
|
+
##
|
|
1226
|
+
# :method: generate
|
|
1227
|
+
# :call-seq: generate(&block)
|
|
1228
|
+
#
|
|
1229
|
+
# Generates one response through Chat#generate without executing tools.
|
|
1230
|
+
# Returns a Message. Exceptions use ::rescue_from handlers.
|
|
1231
|
+
|
|
1232
|
+
##
|
|
1233
|
+
# :method: run_tools
|
|
1234
|
+
# :call-seq: run_tools
|
|
1235
|
+
#
|
|
1236
|
+
# Runs pending tools through Chat#run_tools, respecting approval decisions.
|
|
1237
|
+
# Returns the wrapped chat. Exceptions use ::rescue_from handlers.
|
|
1238
|
+
|
|
1239
|
+
##
|
|
1240
|
+
# :method: step
|
|
1241
|
+
# :call-seq: step(&block)
|
|
1242
|
+
#
|
|
1243
|
+
# Advances the conversation through Chat#step. Returns a Message or
|
|
1244
|
+
# +nil+ when no progress is possible. Exceptions use ::rescue_from handlers.
|
|
1245
|
+
|
|
1246
|
+
##
|
|
1247
|
+
# :method: count_tokens
|
|
1248
|
+
# :call-seq: count_tokens(message = nil)
|
|
1249
|
+
#
|
|
1250
|
+
# Counts request tokens through Chat#count_tokens without generating a
|
|
1251
|
+
# response. Returns an Integer. Exceptions use ::rescue_from handlers.
|
|
1252
|
+
|
|
1253
|
+
##
|
|
1254
|
+
# :method: compact
|
|
1255
|
+
# :call-seq: compact
|
|
1256
|
+
#
|
|
1257
|
+
# Compacts the model context through Chat#compact and returns its Message.
|
|
1258
|
+
# Exceptions use ::rescue_from handlers.
|
|
1259
|
+
|
|
1260
|
+
GUARDED_OPERATIONS.each do |operation|
|
|
1261
|
+
define_method(operation) do |*args, **kwargs, &block|
|
|
1262
|
+
chat.public_send(operation, *args, **kwargs, &block)
|
|
1263
|
+
rescue StandardError => e
|
|
1264
|
+
rescue_with_handler(e)
|
|
1265
|
+
end
|
|
1266
|
+
end
|
|
1267
|
+
|
|
1268
|
+
# Runs the ::rescue_from handler matching +exception+ and returns its
|
|
1269
|
+
# value, or re-raises when no handler matches. The chat operations call
|
|
1270
|
+
# this for you.
|
|
1271
|
+
def rescue_with_handler(exception)
|
|
1272
|
+
handler = self.class.rescue_handler_for(exception)
|
|
1273
|
+
raise exception unless handler
|
|
1274
|
+
|
|
1275
|
+
return instance_exec(exception, &handler) unless handler.is_a?(Symbol)
|
|
1276
|
+
|
|
1277
|
+
handler_method = method(handler)
|
|
1278
|
+
handler_method.arity.zero? ? handler_method.call : handler_method.call(exception)
|
|
1279
|
+
end
|
|
365
1280
|
end
|
|
366
1281
|
end
|