ruby_llm 1.16.0 → 2.0.0.rc2
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 +85 -32
- 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 -8
- 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 +23 -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 +101 -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 -111
- data/lib/ruby_llm/active_record/attachment_helpers.rb +186 -0
- data/lib/ruby_llm/active_record/batch.rb +97 -0
- data/lib/ruby_llm/active_record/chat_methods.rb +828 -305
- data/lib/ruby_llm/active_record/message_methods.rb +113 -136
- 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 -151
- data/lib/ruby_llm/aliases.json +268 -100
- data/lib/ruby_llm/attachment.rb +187 -48
- 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 +1127 -198
- data/lib/ruby_llm/chunk.rb +10 -0
- data/lib/ruby_llm/citation.rb +105 -0
- data/lib/ruby_llm/configuration.rb +261 -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 +121 -17
- data/lib/ruby_llm/embedding_request.rb +53 -0
- data/lib/ruby_llm/error.rb +159 -23
- data/lib/ruby_llm/fallback.rb +133 -0
- data/lib/ruby_llm/files/mime_type.rb +97 -0
- data/lib/ruby_llm/image.rb +153 -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 +24 -5
- data/lib/ruby_llm/model/pricing_category.rb +103 -14
- data/lib/ruby_llm/model/pricing_tier.rb +55 -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 +73261 -33733
- data/lib/ruby_llm/models.rb +477 -215
- 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 +548 -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 +167 -0
- data/lib/ruby_llm/{providers → protocols}/anthropic/tools.rb +24 -41
- data/lib/ruby_llm/protocols/anthropic.rb +101 -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/{providers/openai → protocols/chat_completions}/media.rb +30 -17
- 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 +15 -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 +695 -0
- data/lib/ruby_llm/protocols/converse/media.rb +178 -0
- data/lib/ruby_llm/protocols/converse/streaming.rb +432 -0
- data/lib/ruby_llm/protocols/converse/thinking_stream.rb +56 -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 +17 -11
- 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 +560 -128
- 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 +5 -14
- 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 +60 -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 +193 -41
- data/lib/ruby_llm/providers/bedrock.rb +216 -45
- 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 -51
- data/lib/ruby_llm/providers/deepseek/chat.rb +49 -2
- 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 -16
- 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 -62
- 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 +6 -18
- data/lib/ruby_llm/providers/mistral/models.rb +57 -23
- 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 +16 -4
- data/lib/ruby_llm/providers/ollama/chat.rb +7 -13
- 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 -108
- 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 +2 -9
- data/lib/ruby_llm/providers/perplexity/embeddings.rb +32 -0
- data/lib/ruby_llm/providers/perplexity/media.rb +5 -21
- data/lib/ruby_llm/providers/perplexity/models.rb +80 -13
- data/lib/ruby_llm/providers/perplexity.rb +28 -20
- 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 +160 -17
- data/lib/ruby_llm/providers/xai/capabilities.rb +18 -0
- data/lib/ruby_llm/providers/xai/chat.rb +3 -2
- 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 -36
- 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 +15 -5
- data/lib/ruby_llm/railtie.rb +7 -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 +138 -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 +380 -6
- data/lib/tasks/ruby_llm.rake +21 -16
- data/skills/rubyllm/SKILL.md +81 -0
- data/skills/rubyllm/agents/openai.yaml +4 -0
- metadata +339 -97
- 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 -597
- 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 -41
- data/lib/ruby_llm/connection.rb +0 -159
- data/lib/ruby_llm/content.rb +0 -91
- data/lib/ruby_llm/deprecator.rb +0 -24
- data/lib/ruby_llm/error_middleware.rb +0 -81
- data/lib/ruby_llm/instrumentation.rb +0 -36
- data/lib/ruby_llm/mime_type.rb +0 -96
- data/lib/ruby_llm/model/info.rb +0 -164
- data/lib/ruby_llm/model_registry.rb +0 -39
- data/lib/ruby_llm/models_schema.json +0 -171
- data/lib/ruby_llm/providers/anthropic/chat.rb +0 -291
- 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 -59
- data/lib/ruby_llm/providers/anthropic/streaming.rb +0 -71
- data/lib/ruby_llm/providers/bedrock/chat.rb +0 -405
- data/lib/ruby_llm/providers/bedrock/media.rb +0 -108
- data/lib/ruby_llm/providers/bedrock/streaming.rb +0 -328
- data/lib/ruby_llm/providers/gemini/chat.rb +0 -542
- 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 -98
- data/lib/ruby_llm/providers/gemini/tools.rb +0 -234
- 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 -236
- 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/moderation.rb +0 -34
- data/lib/ruby_llm/providers/openai/streaming.rb +0 -55
- data/lib/ruby_llm/providers/openai/temperature.rb +0 -28
- data/lib/ruby_llm/providers/openai/transcription.rb +0 -71
- 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 -218
- data/lib/ruby_llm/streaming.rb +0 -179
- data/lib/ruby_llm/tool_concurrency.rb +0 -105
- data/lib/ruby_llm/utils.rb +0 -130
- data/lib/tasks/models.rake +0 -593
- data/lib/tasks/release.rake +0 -94
- 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,113 +515,225 @@ module RubyLLM
|
|
|
125
515
|
record
|
|
126
516
|
end
|
|
127
517
|
|
|
128
|
-
#
|
|
129
|
-
|
|
130
|
-
|
|
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
|
|
131
528
|
|
|
132
529
|
input_values, = partition_inputs(kwargs)
|
|
133
530
|
record = chat_or_id.is_a?(resolved_chat_model) ? chat_or_id : resolved_chat_model.find(chat_or_id)
|
|
134
531
|
apply_assume_model_exists(record)
|
|
532
|
+
apply_protocol(record)
|
|
533
|
+
apply_context(record)
|
|
135
534
|
runtime = runtime_context(chat: record, inputs: input_values)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
535
|
+
apply_instructions(
|
|
536
|
+
record,
|
|
537
|
+
runtime,
|
|
538
|
+
inputs: input_values,
|
|
539
|
+
persist: true,
|
|
540
|
+
persistent_only: true
|
|
541
|
+
)
|
|
140
542
|
record
|
|
141
543
|
end
|
|
142
544
|
|
|
143
|
-
def render_prompt(name, chat:, inputs:, locals:)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
|
148
561
|
end
|
|
149
562
|
|
|
150
|
-
|
|
151
|
-
|
|
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)
|
|
152
582
|
end
|
|
153
583
|
|
|
154
584
|
private
|
|
155
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
|
+
|
|
156
624
|
def with_rails_chat_record(method_name, **kwargs)
|
|
157
625
|
raise ArgumentError, 'chat_model must be configured to use create/create!' unless resolved_chat_model
|
|
158
626
|
|
|
159
627
|
input_values, chat_options = partition_inputs(kwargs)
|
|
160
|
-
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
|
+
)
|
|
161
631
|
apply_configuration(record, input_values:, persist_instructions: true) if record
|
|
162
632
|
record
|
|
163
633
|
end
|
|
164
634
|
|
|
165
|
-
def
|
|
166
|
-
|
|
167
|
-
|
|
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]
|
|
168
642
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
|
177
653
|
end
|
|
178
654
|
|
|
179
|
-
|
|
180
|
-
|
|
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?)
|
|
181
659
|
end
|
|
182
660
|
|
|
183
|
-
def
|
|
184
|
-
|
|
185
|
-
|
|
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?
|
|
186
664
|
|
|
187
|
-
|
|
188
|
-
|
|
665
|
+
options = evaluate(tool_options, runtime)
|
|
666
|
+
chat.with_tool_options(**options) if options && !options.empty?
|
|
189
667
|
|
|
190
|
-
|
|
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?
|
|
191
670
|
end
|
|
192
671
|
|
|
193
|
-
def
|
|
194
|
-
|
|
195
|
-
|
|
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
|
|
196
677
|
end
|
|
197
678
|
|
|
198
|
-
def
|
|
199
|
-
|
|
679
|
+
def apply_thinking(chat)
|
|
680
|
+
return if @thinking.nil?
|
|
681
|
+
|
|
682
|
+
@thinking == false ? chat.with_thinking(false) : chat.with_thinking(**@thinking)
|
|
200
683
|
end
|
|
201
684
|
|
|
202
|
-
def
|
|
203
|
-
|
|
685
|
+
def apply_citations(chat)
|
|
686
|
+
chat.with_citations(@citations) unless @citations.nil?
|
|
204
687
|
end
|
|
205
688
|
|
|
206
|
-
def
|
|
207
|
-
value = evaluate(
|
|
208
|
-
|
|
689
|
+
def apply_end_user(chat, runtime)
|
|
690
|
+
value = evaluate(end_user, runtime)
|
|
691
|
+
chat.with_end_user(value) unless value.nil?
|
|
209
692
|
end
|
|
210
693
|
|
|
211
|
-
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)
|
|
212
713
|
value = evaluate(headers, runtime)
|
|
213
|
-
|
|
714
|
+
chat.with_headers(**value) if value && !value.empty?
|
|
214
715
|
end
|
|
215
716
|
|
|
216
|
-
def apply_schema(
|
|
717
|
+
def apply_schema(chat, runtime)
|
|
217
718
|
value = resolved_schema_value(runtime)
|
|
218
|
-
|
|
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?
|
|
219
724
|
end
|
|
220
725
|
|
|
221
726
|
def resolved_schema_value(runtime)
|
|
222
727
|
value = schema
|
|
223
728
|
return value unless value.is_a?(Proc)
|
|
729
|
+
return evaluate(value, runtime) if value.lambda?
|
|
224
730
|
|
|
225
|
-
|
|
226
|
-
rescue NoMethodError => e
|
|
227
|
-
raise unless e.receiver.equal?(runtime)
|
|
228
|
-
|
|
229
|
-
RubyLLM::Schema.create(&value)
|
|
731
|
+
Schematist::Schema.create(&value)
|
|
230
732
|
end
|
|
231
733
|
|
|
232
|
-
def
|
|
233
|
-
apply_assume_model_exists(
|
|
234
|
-
|
|
734
|
+
def apply_chat_options(chat)
|
|
735
|
+
apply_assume_model_exists(chat)
|
|
736
|
+
apply_protocol(chat)
|
|
235
737
|
end
|
|
236
738
|
|
|
237
739
|
def apply_assume_model_exists(chat_object)
|
|
@@ -242,12 +744,20 @@ module RubyLLM
|
|
|
242
744
|
chat_object.assume_model_exists = chat_kwargs[:assume_model_exists]
|
|
243
745
|
end
|
|
244
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
|
+
|
|
245
755
|
def evaluate(value, runtime)
|
|
246
756
|
value.is_a?(Proc) ? runtime.instance_exec(&value) : value
|
|
247
757
|
end
|
|
248
758
|
|
|
249
|
-
def
|
|
250
|
-
value = evaluate(
|
|
759
|
+
def resolved_instruction_value(declaration, chat_object, runtime, inputs:)
|
|
760
|
+
value = evaluate(declaration[:value], runtime)
|
|
251
761
|
return value unless prompt_instruction?(value)
|
|
252
762
|
|
|
253
763
|
runtime.prompt(
|
|
@@ -256,26 +766,32 @@ module RubyLLM
|
|
|
256
766
|
)
|
|
257
767
|
end
|
|
258
768
|
|
|
259
|
-
def
|
|
260
|
-
|
|
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
|
+
}]
|
|
261
779
|
end
|
|
262
780
|
|
|
263
|
-
def
|
|
264
|
-
|
|
265
|
-
chat_object
|
|
266
|
-
else
|
|
267
|
-
runtime_instruction_target(chat_object)
|
|
268
|
-
end
|
|
781
|
+
def instruction_declarations
|
|
782
|
+
@instruction_declarations ||= []
|
|
269
783
|
end
|
|
270
784
|
|
|
271
|
-
def
|
|
272
|
-
|
|
785
|
+
def rails_chat_record?(chat)
|
|
786
|
+
resolved_chat_model && chat.is_a?(resolved_chat_model)
|
|
787
|
+
end
|
|
273
788
|
|
|
274
|
-
|
|
789
|
+
def default_instructions_prompt_exists?
|
|
790
|
+
name && File.exist?(Prompt.new("#{prompt_agent_path}/instructions").path)
|
|
275
791
|
end
|
|
276
792
|
|
|
277
|
-
def
|
|
278
|
-
|
|
793
|
+
def prompt_instruction?(value)
|
|
794
|
+
value.is_a?(Hash) && value[:prompt]
|
|
279
795
|
end
|
|
280
796
|
|
|
281
797
|
def resolve_prompt_locals(locals, runtime:, chat:, inputs:)
|
|
@@ -286,22 +802,6 @@ module RubyLLM
|
|
|
286
802
|
base.merge(evaluated)
|
|
287
803
|
end
|
|
288
804
|
|
|
289
|
-
def partition_inputs(kwargs)
|
|
290
|
-
input_values = {}
|
|
291
|
-
chat_options = {}
|
|
292
|
-
|
|
293
|
-
kwargs.each do |key, value|
|
|
294
|
-
symbolized_key = key.to_sym
|
|
295
|
-
if inputs.include?(symbolized_key)
|
|
296
|
-
input_values[symbolized_key] = value
|
|
297
|
-
else
|
|
298
|
-
chat_options[symbolized_key] = value
|
|
299
|
-
end
|
|
300
|
-
end
|
|
301
|
-
|
|
302
|
-
[input_values, chat_options]
|
|
303
|
-
end
|
|
304
|
-
|
|
305
805
|
def runtime_context(chat:, inputs:)
|
|
306
806
|
agent_class = self
|
|
307
807
|
Object.new.tap do |runtime|
|
|
@@ -316,27 +816,9 @@ module RubyLLM
|
|
|
316
816
|
end
|
|
317
817
|
end
|
|
318
818
|
|
|
319
|
-
def prompt_path_for(name)
|
|
320
|
-
filename = name.to_s
|
|
321
|
-
filename += '.txt.erb' unless filename.end_with?('.txt.erb')
|
|
322
|
-
prompt_root.join(prompt_agent_path, filename)
|
|
323
|
-
end
|
|
324
|
-
|
|
325
819
|
def prompt_agent_path
|
|
326
820
|
class_name = name || 'agent'
|
|
327
|
-
class_name.gsub('::', '/')
|
|
328
|
-
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
329
|
-
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
330
|
-
.tr('-', '_')
|
|
331
|
-
.downcase
|
|
332
|
-
end
|
|
333
|
-
|
|
334
|
-
def prompt_root
|
|
335
|
-
if defined?(Rails) && Rails.respond_to?(:root) && Rails.root
|
|
336
|
-
Rails.root.join('app/prompts')
|
|
337
|
-
else
|
|
338
|
-
Pathname.new(Dir.pwd).join('app/prompts')
|
|
339
|
-
end
|
|
821
|
+
Support::Utils.underscore(class_name.gsub('::', '/')).tr('-', '_')
|
|
340
822
|
end
|
|
341
823
|
|
|
342
824
|
def resolved_chat_model
|
|
@@ -349,19 +831,451 @@ module RubyLLM
|
|
|
349
831
|
end
|
|
350
832
|
end
|
|
351
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
|
+
#
|
|
352
848
|
def initialize(chat: nil, inputs: nil, persist_instructions: true, **kwargs)
|
|
353
|
-
input_values, chat_options = self.class.
|
|
354
|
-
|
|
355
|
-
self.class.
|
|
356
|
-
|
|
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:)
|
|
357
853
|
end
|
|
358
854
|
|
|
855
|
+
# The wrapped Chat, or the chat record in Rails mode.
|
|
359
856
|
attr_reader :chat
|
|
360
857
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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
|
|
366
1280
|
end
|
|
367
1281
|
end
|