langchainrb 0.15.3 → 0.15.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +87 -54
- data/lib/langchain/assistants/assistant.rb +216 -29
- data/lib/langchain/assistants/messages/mistral_ai_message.rb +74 -0
- data/lib/langchain/assistants/thread.rb +7 -2
- data/lib/langchain/llm/azure.rb +3 -0
- data/lib/langchain/llm/google_gemini.rb +0 -1
- data/lib/langchain/llm/mistral_ai.rb +1 -1
- data/lib/langchain/llm/ollama.rb +3 -3
- data/lib/langchain/llm/response/mistral_ai_response.rb +9 -1
- data/lib/langchain/llm/response/ollama_response.rb +1 -1
- data/lib/langchain/output_parsers/structured_output_parser.rb +0 -1
- data/lib/langchain/processors/eml.rb +1 -1
- data/lib/langchain/prompt/base.rb +0 -1
- data/lib/langchain/prompt/loading.rb +0 -1
- data/lib/langchain/prompt/prompt_template.rb +2 -1
- data/lib/langchain/tool/database.rb +48 -28
- data/lib/langchain/tool_definition.rb +10 -12
- data/lib/langchain/vectorsearch/epsilla.rb +0 -2
- data/lib/langchain/version.rb +1 -1
- data/lib/langchain.rb +5 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cb478a5261da82b78d6a90b6179e4050ae0fcba274c370b507f546b59cfd1a78
|
4
|
+
data.tar.gz: 7fb27d8b3c68060e923e69b1c67680be20846e5a384def4c6ce0efd1c9fa56ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b6533a4064e822c78cc7659c55c948b13b4904d75ab99fb50269f82d7f815e02ff3c0659f0b6c1905b16c0834f2577ccd136cb3d1a34db6e26b7352c77350a10
|
7
|
+
data.tar.gz: 170e767e0fdef21fc6051a161904ee867205bb18a98641b545adf7fe3933b499d157be52009fd20e168efa5d94e5a03c39a1a21c94c2747b8e6a0393f2099e4e
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.15.4] - 2024-09-10 🇧🇦
|
4
|
+
- Fix for Langchain::Prompt::PromptTemplate supporting nested JSON data
|
5
|
+
- Require common libs at top-level
|
6
|
+
- Add `add_message_callback` to `Langchain::Assistant` constructor to invoke an optional function when any message is added to the conversation
|
7
|
+
- Adding Assistant syntactic sugar with #run! and #add_message_and_run!
|
8
|
+
|
9
|
+
## [0.15.4] - 2024-08-30
|
10
|
+
- Improve the Langchain::Tool::Database tool
|
11
|
+
- Allow explictly setting tool_choice on the Assistant instance
|
12
|
+
- Add support for bulk embedding in Ollama
|
13
|
+
- `Langchain::Assistant` works with `Langchain::LLM::MistralAI` llm
|
14
|
+
- Fix Langchain::LLM::Azure not applying full default_options
|
15
|
+
|
3
16
|
## [0.15.3] - 2024-08-27
|
4
17
|
- Fix OpenAI#embed when text-embedding-ada-002 is used
|
5
18
|
|
data/README.md
CHANGED
@@ -402,75 +402,108 @@ client.ask(question: "...")
|
|
402
402
|
```
|
403
403
|
|
404
404
|
## Assistants
|
405
|
-
|
406
|
-
|
407
|
-
### Available Tools 🛠️
|
408
|
-
|
409
|
-
| Name | Description | ENV Requirements | Gem Requirements |
|
410
|
-
| ------------ | :------------------------------------------------: | :-----------------------------------------------------------: | :---------------------------------------: |
|
411
|
-
| "calculator" | Useful for getting the result of a math expression | | `gem "eqn", "~> 1.6.5"` |
|
412
|
-
| "database" | Useful for querying a SQL database | | `gem "sequel", "~> 5.68.0"` |
|
413
|
-
| "file_system" | Interacts with the file system | | |
|
414
|
-
| "ruby_code_interpreter" | Interprets Ruby expressions | | `gem "safe_ruby", "~> 1.0.4"` |
|
415
|
-
| "google_search" | A wrapper around Google Search | `ENV["SERPAPI_API_KEY"]` (https://serpapi.com/manage-api-key) | `gem "google_search_results", "~> 2.0.0"` |
|
416
|
-
| "news_retriever" | A wrapper around NewsApi.org | `ENV["NEWS_API_KEY"]` (https://newsapi.org/) | |
|
417
|
-
| "tavily" | A wrapper around Tavily AI | `ENV["TAVILY_API_KEY"]` (https://tavily.com/) | |
|
418
|
-
| "weather" | Calls Open Weather API to retrieve the current weather | `ENV["OPEN_WEATHER_API_KEY"]` (https://home.openweathermap.org/api_keys) | |
|
419
|
-
| "wikipedia" | Calls Wikipedia API to retrieve the summary | | `gem "wikipedia-client", "~> 1.17.0"` |
|
405
|
+
`Langchain::Assistant` is a powerful and flexible class that combines Large Language Models (LLMs), tools, and conversation management to create intelligent, interactive assistants. It's designed to handle complex conversations, execute tools, and provide coherent responses based on the context of the interaction.
|
420
406
|
|
421
|
-
###
|
422
|
-
|
423
|
-
|
424
|
-
|
407
|
+
### Features
|
408
|
+
* Supports multiple LLM providers (OpenAI, Google Gemini, Anthropic, Mistral AI and open-source models via Ollama)
|
409
|
+
* Integrates with various tools to extend functionality
|
410
|
+
* Manages conversation threads
|
411
|
+
* Handles automatic and manual tool execution
|
412
|
+
* Supports different message formats for various LLM providers
|
425
413
|
|
426
|
-
###
|
427
|
-
1. Instantiate an LLM of your choice
|
414
|
+
### Usage
|
428
415
|
```ruby
|
429
416
|
llm = Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
|
430
|
-
```
|
431
|
-
2. Instantiate an Assistant
|
432
|
-
```ruby
|
433
417
|
assistant = Langchain::Assistant.new(
|
434
418
|
llm: llm,
|
435
|
-
instructions: "You
|
436
|
-
tools: [
|
437
|
-
Langchain::Tool::Weather.new(api_key: ENV["OPEN_WEATHER_API_KEY"])
|
438
|
-
]
|
419
|
+
instructions: "You're a helpful AI assistant",
|
420
|
+
tools: [Langchain::Tool::NewsRetriever.new(api_key: ENV["NEWS_API_KEY"])]
|
439
421
|
)
|
440
|
-
```
|
441
|
-
### Using an Assistant
|
442
|
-
You can now add your message to an Assistant.
|
443
|
-
```ruby
|
444
|
-
assistant.add_message content: "What's the weather in New York, New York?"
|
445
|
-
```
|
446
422
|
|
447
|
-
|
448
|
-
|
449
|
-
|
423
|
+
# Add a user message and run the assistant
|
424
|
+
assistant.add_message_and_run(content: "What's the latest news about AI?")
|
425
|
+
|
426
|
+
# Access the conversation thread
|
427
|
+
messages = assistant.messages
|
428
|
+
|
429
|
+
# Run the assistant with automatic tool execution
|
430
|
+
assistant.run!
|
431
|
+
```
|
432
|
+
|
433
|
+
### Configuration
|
434
|
+
* `llm`: The LLM instance to use (required)
|
435
|
+
* `tools`: An array of tool instances (optional)
|
436
|
+
* `instructions`: System instructions for the assistant (optional)
|
437
|
+
* `tool_choice`: Specifies how tools should be selected. Default: "auto". A specific tool function name can be passed. This will force the Assistant to **always** use this function.
|
438
|
+
* `add_message_callback`: A callback function (proc, lambda) that is called when any message is added to the conversation (optional)
|
439
|
+
|
440
|
+
### Key Methods
|
441
|
+
* `add_message`: Adds a user message to the messages array
|
442
|
+
* `run!`: Processes the conversation and generates responses
|
443
|
+
* `add_message_and_run!`: Combines adding a message and running the assistant
|
444
|
+
* `submit_tool_output`: Manually submit output to a tool call
|
445
|
+
* `messages`: Returns a list of ongoing messages
|
446
|
+
|
447
|
+
### Built-in Tools 🛠️
|
448
|
+
* `Langchain::Tool::Calculator`: Useful for evaluating math expressions. Requires `gem "eqn"`.
|
449
|
+
* `Langchain::Tool::Database`: Connect your SQL database. Requires `gem "sequel"`.
|
450
|
+
* `Langchain::Tool::FileSystem`: Interact with the file system (read & write).
|
451
|
+
* `Langchain::Tool::RubyCodeInterpreter`: Useful for evaluating generated Ruby code. Requires `gem "safe_ruby"` (In need of a better solution).
|
452
|
+
* `Langchain::Tool::NewsRetriever`: A wrapper around [NewsApi.org](https://newsapi.org) to fetch news articles.
|
453
|
+
* `Langchain::Tool::Tavily`: A wrapper around [Tavily AI](https://tavily.com).
|
454
|
+
* `Langchain::Tool::Weather`: Calls [Open Weather API](https://home.openweathermap.org) to retrieve the current weather.
|
455
|
+
* `Langchain::Tool::Wikipedia`: Calls Wikipedia API.
|
456
|
+
|
457
|
+
### Creating custom Tools
|
458
|
+
The Langchain::Assistant can be easily extended with custom tools by creating classes that `extend Langchain::ToolDefinition` module and implement required methods.
|
459
|
+
```ruby
|
460
|
+
class MovieInfoTool
|
461
|
+
include Langchain::ToolDefinition
|
462
|
+
|
463
|
+
define_function :search_movie, description: "MovieInfoTool: Search for a movie by title" do
|
464
|
+
property :query, type: "string", description: "The movie title to search for", required: true
|
465
|
+
end
|
466
|
+
|
467
|
+
define_function :get_movie_details, description: "MovieInfoTool: Get detailed information about a specific movie" do
|
468
|
+
property :movie_id, type: "integer", description: "The TMDb ID of the movie", required: true
|
469
|
+
end
|
470
|
+
|
471
|
+
def initialize(api_key:)
|
472
|
+
@api_key = api_key
|
473
|
+
end
|
474
|
+
|
475
|
+
def search_movie(query:)
|
476
|
+
...
|
477
|
+
end
|
478
|
+
|
479
|
+
def get_movie_details(movie_id:)
|
480
|
+
...
|
481
|
+
end
|
482
|
+
end
|
450
483
|
```
|
451
484
|
|
452
|
-
|
485
|
+
#### Example usage:
|
453
486
|
```ruby
|
454
|
-
|
455
|
-
```
|
487
|
+
movie_tool = MovieInfoTool.new(api_key: "...")
|
456
488
|
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
You can also combine the two by calling:
|
463
|
-
```ruby
|
464
|
-
assistant.add_message_and_run content: "What about Sacramento, CA?", auto_tool_execution: true
|
465
|
-
```
|
489
|
+
assistant = Langchain::Assistant.new(
|
490
|
+
llm: llm,
|
491
|
+
instructions: "You're a helpful AI assistant that can provide movie information",
|
492
|
+
tools: [movie_tool]
|
493
|
+
)
|
466
494
|
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
assistant.messages
|
495
|
+
assistant.add_message_and_run(content: "Can you tell me about the movie 'Inception'?")
|
496
|
+
# Check the response in the last message in the conversation
|
497
|
+
assistant.messages.last
|
471
498
|
```
|
472
499
|
|
473
|
-
|
500
|
+
### Error Handling
|
501
|
+
The assistant includes error handling for invalid inputs, unsupported LLM types, and tool execution failures. It uses a state machine to manage the conversation flow and handle different scenarios gracefully.
|
502
|
+
|
503
|
+
### Demos
|
504
|
+
1. [Building an AI Assistant that operates a simulated E-commerce Store](https://www.loom.com/share/83aa4fd8dccb492aad4ca95da40ed0b2)
|
505
|
+
2. [New Langchain.rb Assistants interface](https://www.loom.com/share/e883a4a49b8746c1b0acf9d58cf6da36)
|
506
|
+
3. [Langchain.rb Assistant demo with NewsRetriever and function calling on Gemini](https://youtu.be/-ieyahrpDpM&t=1477s) - [code](https://github.com/palladius/gemini-news-crawler)
|
474
507
|
|
475
508
|
## Evaluations (Evals)
|
476
509
|
The Evaluations module is a collection of tools that can be used to evaluate and track the performance of the output products by LLM and your RAG (Retrieval Augmented Generation) pipelines.
|
@@ -15,7 +15,7 @@ module Langchain
|
|
15
15
|
extend Forwardable
|
16
16
|
def_delegators :thread, :messages
|
17
17
|
|
18
|
-
attr_reader :llm, :thread, :instructions, :state
|
18
|
+
attr_reader :llm, :thread, :instructions, :state, :llm_adapter, :tool_choice
|
19
19
|
attr_reader :total_prompt_tokens, :total_completion_tokens, :total_tokens
|
20
20
|
attr_accessor :tools
|
21
21
|
|
@@ -29,7 +29,9 @@ module Langchain
|
|
29
29
|
llm:,
|
30
30
|
thread: nil,
|
31
31
|
tools: [],
|
32
|
-
instructions: nil
|
32
|
+
instructions: nil,
|
33
|
+
tool_choice: "auto",
|
34
|
+
add_message_callback: nil
|
33
35
|
)
|
34
36
|
unless tools.is_a?(Array) && tools.all? { |tool| tool.class.singleton_class.included_modules.include?(Langchain::ToolDefinition) }
|
35
37
|
raise ArgumentError, "Tools must be an array of objects extending Langchain::ToolDefinition"
|
@@ -37,8 +39,12 @@ module Langchain
|
|
37
39
|
|
38
40
|
@llm = llm
|
39
41
|
@llm_adapter = LLM::Adapter.build(llm)
|
42
|
+
|
40
43
|
@thread = thread || Langchain::Thread.new
|
44
|
+
@thread.add_message_callback = add_message_callback
|
45
|
+
|
41
46
|
@tools = tools
|
47
|
+
self.tool_choice = tool_choice
|
42
48
|
@instructions = instructions
|
43
49
|
@state = :ready
|
44
50
|
|
@@ -49,9 +55,8 @@ module Langchain
|
|
49
55
|
raise ArgumentError, "Thread must be an instance of Langchain::Thread" unless @thread.is_a?(Langchain::Thread)
|
50
56
|
|
51
57
|
# The first message in the thread should be the system instructions
|
52
|
-
# TODO: What if the user added old messages and the system instructions are already in there? Should this overwrite the existing instructions?
|
53
|
-
initialize_instructions
|
54
58
|
# For Google Gemini, and Anthropic system instructions are added to the `system:` param in the `chat` method
|
59
|
+
initialize_instructions
|
55
60
|
end
|
56
61
|
|
57
62
|
# Add a user message to the thread
|
@@ -105,6 +110,13 @@ module Langchain
|
|
105
110
|
thread.messages
|
106
111
|
end
|
107
112
|
|
113
|
+
# Run the assistant with automatic tool execution
|
114
|
+
#
|
115
|
+
# @return [Array<Langchain::Message>] The messages in the thread
|
116
|
+
def run!
|
117
|
+
run(auto_tool_execution: true)
|
118
|
+
end
|
119
|
+
|
108
120
|
# Add a user message to the thread and run the assistant
|
109
121
|
#
|
110
122
|
# @param content [String] The content of the message
|
@@ -115,6 +127,14 @@ module Langchain
|
|
115
127
|
run(auto_tool_execution: auto_tool_execution)
|
116
128
|
end
|
117
129
|
|
130
|
+
# Add a user message to the thread and run the assistant with automatic tool execution
|
131
|
+
#
|
132
|
+
# @param content [String] The content of the message
|
133
|
+
# @return [Array<Langchain::Message>] The messages in the thread
|
134
|
+
def add_message_and_run!(content:)
|
135
|
+
add_message_and_run(content: content, auto_tool_execution: true)
|
136
|
+
end
|
137
|
+
|
118
138
|
# Submit tool output to the thread
|
119
139
|
#
|
120
140
|
# @param tool_call_id [String] The ID of the tool call to submit output for
|
@@ -150,8 +170,21 @@ module Langchain
|
|
150
170
|
thread.messages.unshift(message)
|
151
171
|
end
|
152
172
|
|
173
|
+
def tool_choice=(new_tool_choice)
|
174
|
+
validate_tool_choice!(new_tool_choice)
|
175
|
+
@tool_choice = new_tool_choice
|
176
|
+
end
|
177
|
+
|
153
178
|
private
|
154
179
|
|
180
|
+
# TODO: If tool_choice = "tool_function_name" and then tool is removed from the assistant, should we set tool_choice back to "auto"?
|
181
|
+
def validate_tool_choice!(tool_choice)
|
182
|
+
allowed_tool_choices = llm_adapter.allowed_tool_choices.concat(available_tool_names)
|
183
|
+
unless allowed_tool_choices.include?(tool_choice)
|
184
|
+
raise ArgumentError, "Tool choice must be one of: #{allowed_tool_choices.join(", ")}"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
155
188
|
# Check if the run is finished
|
156
189
|
#
|
157
190
|
# @param auto_tool_execution [Boolean] Whether or not to automatically run tools
|
@@ -257,20 +290,22 @@ module Langchain
|
|
257
290
|
# @return [String] The tool role
|
258
291
|
def determine_tool_role
|
259
292
|
case llm
|
293
|
+
when Langchain::LLM::Anthropic
|
294
|
+
Langchain::Messages::AnthropicMessage::TOOL_ROLE
|
295
|
+
when Langchain::LLM::GoogleGemini, Langchain::LLM::GoogleVertexAI
|
296
|
+
Langchain::Messages::GoogleGeminiMessage::TOOL_ROLE
|
297
|
+
when Langchain::LLM::MistralAI
|
298
|
+
Langchain::Messages::MistralAIMessage::TOOL_ROLE
|
260
299
|
when Langchain::LLM::Ollama
|
261
300
|
Langchain::Messages::OllamaMessage::TOOL_ROLE
|
262
301
|
when Langchain::LLM::OpenAI
|
263
302
|
Langchain::Messages::OpenAIMessage::TOOL_ROLE
|
264
|
-
when Langchain::LLM::GoogleGemini, Langchain::LLM::GoogleVertexAI
|
265
|
-
Langchain::Messages::GoogleGeminiMessage::TOOL_ROLE
|
266
|
-
when Langchain::LLM::Anthropic
|
267
|
-
Langchain::Messages::AnthropicMessage::TOOL_ROLE
|
268
303
|
end
|
269
304
|
end
|
270
305
|
|
271
306
|
def initialize_instructions
|
272
|
-
if llm.is_a?(Langchain::LLM::OpenAI)
|
273
|
-
|
307
|
+
if llm.is_a?(Langchain::LLM::OpenAI) || llm.is_a?(Langchain::LLM::MistralAI)
|
308
|
+
self.instructions = @instructions if @instructions
|
274
309
|
end
|
275
310
|
end
|
276
311
|
|
@@ -281,9 +316,10 @@ module Langchain
|
|
281
316
|
Langchain.logger.info("Sending a call to #{llm.class}", for: self.class)
|
282
317
|
|
283
318
|
params = @llm_adapter.build_chat_params(
|
284
|
-
tools: @tools,
|
285
319
|
instructions: @instructions,
|
286
|
-
messages: thread.array_of_message_hashes
|
320
|
+
messages: thread.array_of_message_hashes,
|
321
|
+
tools: @tools,
|
322
|
+
tool_choice: tool_choice
|
287
323
|
)
|
288
324
|
@llm.chat(**params)
|
289
325
|
end
|
@@ -298,7 +334,7 @@ module Langchain
|
|
298
334
|
|
299
335
|
tool_instance = tools.find do |t|
|
300
336
|
t.class.tool_name == tool_name
|
301
|
-
end or raise ArgumentError, "Tool not found in assistant.tools"
|
337
|
+
end or raise ArgumentError, "Tool: #{tool_name} not found in assistant.tools"
|
302
338
|
|
303
339
|
output = tool_instance.send(method_name, **tool_arguments)
|
304
340
|
|
@@ -329,20 +365,26 @@ module Langchain
|
|
329
365
|
@total_tokens += total_tokens_from_operation if total_tokens_from_operation
|
330
366
|
end
|
331
367
|
|
368
|
+
def available_tool_names
|
369
|
+
llm_adapter.available_tool_names(tools)
|
370
|
+
end
|
371
|
+
|
332
372
|
# TODO: Fix the message truncation when context window is exceeded
|
333
373
|
|
334
374
|
module LLM
|
335
375
|
class Adapter
|
336
376
|
def self.build(llm)
|
337
377
|
case llm
|
378
|
+
when Langchain::LLM::Anthropic
|
379
|
+
Adapters::Anthropic.new
|
380
|
+
when Langchain::LLM::GoogleGemini, Langchain::LLM::GoogleVertexAI
|
381
|
+
Adapters::GoogleGemini.new
|
382
|
+
when Langchain::LLM::MistralAI
|
383
|
+
Adapters::MistralAI.new
|
338
384
|
when Langchain::LLM::Ollama
|
339
385
|
Adapters::Ollama.new
|
340
386
|
when Langchain::LLM::OpenAI
|
341
387
|
Adapters::OpenAI.new
|
342
|
-
when Langchain::LLM::GoogleGemini, Langchain::LLM::GoogleVertexAI
|
343
|
-
Adapters::GoogleGemini.new
|
344
|
-
when Langchain::LLM::Anthropic
|
345
|
-
Adapters::Anthropic.new
|
346
388
|
else
|
347
389
|
raise ArgumentError, "Unsupported LLM type: #{llm.class}"
|
348
390
|
end
|
@@ -351,7 +393,7 @@ module Langchain
|
|
351
393
|
|
352
394
|
module Adapters
|
353
395
|
class Base
|
354
|
-
def build_chat_params(tools:, instructions:, messages:)
|
396
|
+
def build_chat_params(tools:, instructions:, messages:, tool_choice:)
|
355
397
|
raise NotImplementedError, "Subclasses must implement build_chat_params"
|
356
398
|
end
|
357
399
|
|
@@ -365,10 +407,10 @@ module Langchain
|
|
365
407
|
end
|
366
408
|
|
367
409
|
class Ollama < Base
|
368
|
-
def build_chat_params(tools:, instructions:, messages:)
|
410
|
+
def build_chat_params(tools:, instructions:, messages:, tool_choice:)
|
369
411
|
params = {messages: messages}
|
370
412
|
if tools.any?
|
371
|
-
params[:tools] = tools
|
413
|
+
params[:tools] = build_tools(tools)
|
372
414
|
end
|
373
415
|
params
|
374
416
|
end
|
@@ -396,14 +438,28 @@ module Langchain
|
|
396
438
|
|
397
439
|
[tool_call_id, tool_name, method_name, tool_arguments]
|
398
440
|
end
|
441
|
+
|
442
|
+
def available_tool_names(tools)
|
443
|
+
build_tools(tools).map { |tool| tool.dig(:function, :name) }
|
444
|
+
end
|
445
|
+
|
446
|
+
def allowed_tool_choices
|
447
|
+
["auto", "none"]
|
448
|
+
end
|
449
|
+
|
450
|
+
private
|
451
|
+
|
452
|
+
def build_tools(tools)
|
453
|
+
tools.map { |tool| tool.class.function_schemas.to_openai_format }.flatten
|
454
|
+
end
|
399
455
|
end
|
400
456
|
|
401
457
|
class OpenAI < Base
|
402
|
-
def build_chat_params(tools:, instructions:, messages:)
|
458
|
+
def build_chat_params(tools:, instructions:, messages:, tool_choice:)
|
403
459
|
params = {messages: messages}
|
404
460
|
if tools.any?
|
405
|
-
params[:tools] = tools
|
406
|
-
params[:tool_choice] =
|
461
|
+
params[:tools] = build_tools(tools)
|
462
|
+
params[:tool_choice] = build_tool_choice(tool_choice)
|
407
463
|
end
|
408
464
|
params
|
409
465
|
end
|
@@ -431,15 +487,96 @@ module Langchain
|
|
431
487
|
|
432
488
|
[tool_call_id, tool_name, method_name, tool_arguments]
|
433
489
|
end
|
490
|
+
|
491
|
+
def build_tools(tools)
|
492
|
+
tools.map { |tool| tool.class.function_schemas.to_openai_format }.flatten
|
493
|
+
end
|
494
|
+
|
495
|
+
def allowed_tool_choices
|
496
|
+
["auto", "none"]
|
497
|
+
end
|
498
|
+
|
499
|
+
def available_tool_names(tools)
|
500
|
+
build_tools(tools).map { |tool| tool.dig(:function, :name) }
|
501
|
+
end
|
502
|
+
|
503
|
+
private
|
504
|
+
|
505
|
+
def build_tool_choice(choice)
|
506
|
+
case choice
|
507
|
+
when "auto"
|
508
|
+
choice
|
509
|
+
else
|
510
|
+
{"type" => "function", "function" => {"name" => choice}}
|
511
|
+
end
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
class MistralAI < Base
|
516
|
+
def build_chat_params(tools:, instructions:, messages:, tool_choice:)
|
517
|
+
params = {messages: messages}
|
518
|
+
if tools.any?
|
519
|
+
params[:tools] = build_tools(tools)
|
520
|
+
params[:tool_choice] = build_tool_choice(tool_choice)
|
521
|
+
end
|
522
|
+
params
|
523
|
+
end
|
524
|
+
|
525
|
+
def build_message(role:, content: nil, tool_calls: [], tool_call_id: nil)
|
526
|
+
Langchain::Messages::MistralAIMessage.new(role: role, content: content, tool_calls: tool_calls, tool_call_id: tool_call_id)
|
527
|
+
end
|
528
|
+
|
529
|
+
# Extract the tool call information from the OpenAI tool call hash
|
530
|
+
#
|
531
|
+
# @param tool_call [Hash] The tool call hash
|
532
|
+
# @return [Array] The tool call information
|
533
|
+
def extract_tool_call_args(tool_call:)
|
534
|
+
tool_call_id = tool_call.dig("id")
|
535
|
+
|
536
|
+
function_name = tool_call.dig("function", "name")
|
537
|
+
tool_name, method_name = function_name.split("__")
|
538
|
+
|
539
|
+
tool_arguments = tool_call.dig("function", "arguments")
|
540
|
+
tool_arguments = if tool_arguments.is_a?(Hash)
|
541
|
+
Langchain::Utils::HashTransformer.symbolize_keys(tool_arguments)
|
542
|
+
else
|
543
|
+
JSON.parse(tool_arguments, symbolize_names: true)
|
544
|
+
end
|
545
|
+
|
546
|
+
[tool_call_id, tool_name, method_name, tool_arguments]
|
547
|
+
end
|
548
|
+
|
549
|
+
def build_tools(tools)
|
550
|
+
tools.map { |tool| tool.class.function_schemas.to_openai_format }.flatten
|
551
|
+
end
|
552
|
+
|
553
|
+
def allowed_tool_choices
|
554
|
+
["auto", "none"]
|
555
|
+
end
|
556
|
+
|
557
|
+
def available_tool_names(tools)
|
558
|
+
build_tools(tools).map { |tool| tool.dig(:function, :name) }
|
559
|
+
end
|
560
|
+
|
561
|
+
private
|
562
|
+
|
563
|
+
def build_tool_choice(choice)
|
564
|
+
case choice
|
565
|
+
when "auto"
|
566
|
+
choice
|
567
|
+
else
|
568
|
+
{"type" => "function", "function" => {"name" => choice}}
|
569
|
+
end
|
570
|
+
end
|
434
571
|
end
|
435
572
|
|
436
573
|
class GoogleGemini < Base
|
437
|
-
def build_chat_params(tools:, instructions:, messages:)
|
574
|
+
def build_chat_params(tools:, instructions:, messages:, tool_choice:)
|
438
575
|
params = {messages: messages}
|
439
576
|
if tools.any?
|
440
|
-
params[:tools] = tools
|
577
|
+
params[:tools] = build_tools(tools)
|
441
578
|
params[:system] = instructions if instructions
|
442
|
-
params[:tool_choice] =
|
579
|
+
params[:tool_choice] = build_tool_config(tool_choice)
|
443
580
|
end
|
444
581
|
params
|
445
582
|
end
|
@@ -459,14 +596,39 @@ module Langchain
|
|
459
596
|
tool_arguments = tool_call.dig("functionCall", "args").transform_keys(&:to_sym)
|
460
597
|
[tool_call_id, tool_name, method_name, tool_arguments]
|
461
598
|
end
|
599
|
+
|
600
|
+
def build_tools(tools)
|
601
|
+
tools.map { |tool| tool.class.function_schemas.to_google_gemini_format }.flatten
|
602
|
+
end
|
603
|
+
|
604
|
+
def allowed_tool_choices
|
605
|
+
["auto", "none"]
|
606
|
+
end
|
607
|
+
|
608
|
+
def available_tool_names(tools)
|
609
|
+
build_tools(tools).map { |tool| tool.dig(:name) }
|
610
|
+
end
|
611
|
+
|
612
|
+
private
|
613
|
+
|
614
|
+
def build_tool_config(choice)
|
615
|
+
case choice
|
616
|
+
when "auto"
|
617
|
+
{function_calling_config: {mode: "auto"}}
|
618
|
+
when "none"
|
619
|
+
{function_calling_config: {mode: "none"}}
|
620
|
+
else
|
621
|
+
{function_calling_config: {mode: "any", allowed_function_names: [choice]}}
|
622
|
+
end
|
623
|
+
end
|
462
624
|
end
|
463
625
|
|
464
626
|
class Anthropic < Base
|
465
|
-
def build_chat_params(tools:, instructions:, messages:)
|
627
|
+
def build_chat_params(tools:, instructions:, messages:, tool_choice:)
|
466
628
|
params = {messages: messages}
|
467
629
|
if tools.any?
|
468
|
-
params[:tools] = tools
|
469
|
-
params[:tool_choice] =
|
630
|
+
params[:tools] = build_tools(tools)
|
631
|
+
params[:tool_choice] = build_tool_choice(tool_choice)
|
470
632
|
end
|
471
633
|
params[:system] = instructions if instructions
|
472
634
|
params
|
@@ -487,6 +649,31 @@ module Langchain
|
|
487
649
|
tool_arguments = tool_call.dig("input").transform_keys(&:to_sym)
|
488
650
|
[tool_call_id, tool_name, method_name, tool_arguments]
|
489
651
|
end
|
652
|
+
|
653
|
+
def build_tools(tools)
|
654
|
+
tools.map { |tool| tool.class.function_schemas.to_anthropic_format }.flatten
|
655
|
+
end
|
656
|
+
|
657
|
+
def allowed_tool_choices
|
658
|
+
["auto", "any"]
|
659
|
+
end
|
660
|
+
|
661
|
+
def available_tool_names(tools)
|
662
|
+
build_tools(tools).map { |tool| tool.dig(:name) }
|
663
|
+
end
|
664
|
+
|
665
|
+
private
|
666
|
+
|
667
|
+
def build_tool_choice(choice)
|
668
|
+
case choice
|
669
|
+
when "auto"
|
670
|
+
{type: "auto"}
|
671
|
+
when "any"
|
672
|
+
{type: "any"}
|
673
|
+
else
|
674
|
+
{type: "tool", name: choice}
|
675
|
+
end
|
676
|
+
end
|
490
677
|
end
|
491
678
|
end
|
492
679
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Langchain
|
4
|
+
module Messages
|
5
|
+
class MistralAIMessage < Base
|
6
|
+
# MistralAI uses the following roles:
|
7
|
+
ROLES = [
|
8
|
+
"system",
|
9
|
+
"assistant",
|
10
|
+
"user",
|
11
|
+
"tool"
|
12
|
+
].freeze
|
13
|
+
|
14
|
+
TOOL_ROLE = "tool"
|
15
|
+
|
16
|
+
# Initialize a new MistralAI message
|
17
|
+
#
|
18
|
+
# @param [String] The role of the message
|
19
|
+
# @param [String] The content of the message
|
20
|
+
# @param [Array<Hash>] The tool calls made in the message
|
21
|
+
# @param [String] The ID of the tool call
|
22
|
+
def initialize(role:, content: nil, tool_calls: [], tool_call_id: nil) # TODO: Implement image_file: reference (https://platform.openai.com/docs/api-reference/messages/object#messages/object-content)
|
23
|
+
raise ArgumentError, "Role must be one of #{ROLES.join(", ")}" unless ROLES.include?(role)
|
24
|
+
raise ArgumentError, "Tool calls must be an array of hashes" unless tool_calls.is_a?(Array) && tool_calls.all? { |tool_call| tool_call.is_a?(Hash) }
|
25
|
+
|
26
|
+
@role = role
|
27
|
+
# Some Tools return content as a JSON hence `.to_s`
|
28
|
+
@content = content.to_s
|
29
|
+
@tool_calls = tool_calls
|
30
|
+
@tool_call_id = tool_call_id
|
31
|
+
end
|
32
|
+
|
33
|
+
# Check if the message came from an LLM
|
34
|
+
#
|
35
|
+
# @return [Boolean] true/false whether this message was produced by an LLM
|
36
|
+
def llm?
|
37
|
+
assistant?
|
38
|
+
end
|
39
|
+
|
40
|
+
# Convert the message to an MistralAI API-compatible hash
|
41
|
+
#
|
42
|
+
# @return [Hash] The message as an MistralAI API-compatible hash
|
43
|
+
def to_hash
|
44
|
+
{}.tap do |h|
|
45
|
+
h[:role] = role
|
46
|
+
h[:content] = content if content # Content is nil for tool calls
|
47
|
+
h[:tool_calls] = tool_calls if tool_calls.any?
|
48
|
+
h[:tool_call_id] = tool_call_id if tool_call_id
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Check if the message came from an LLM
|
53
|
+
#
|
54
|
+
# @return [Boolean] true/false whether this message was produced by an LLM
|
55
|
+
def assistant?
|
56
|
+
role == "assistant"
|
57
|
+
end
|
58
|
+
|
59
|
+
# Check if the message are system instructions
|
60
|
+
#
|
61
|
+
# @return [Boolean] true/false whether this message are system instructions
|
62
|
+
def system?
|
63
|
+
role == "system"
|
64
|
+
end
|
65
|
+
|
66
|
+
# Check if the message is a tool call
|
67
|
+
#
|
68
|
+
# @return [Boolean] true/false whether this message is a tool call
|
69
|
+
def tool?
|
70
|
+
role == "tool"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -4,12 +4,14 @@ module Langchain
|
|
4
4
|
# Langchain::Thread keeps track of messages in a conversation.
|
5
5
|
# TODO: Add functionality to persist to the thread to disk, DB, storage, etc.
|
6
6
|
class Thread
|
7
|
-
attr_accessor :messages
|
7
|
+
attr_accessor :messages, :add_message_callback
|
8
8
|
|
9
9
|
# @param messages [Array<Langchain::Message>]
|
10
|
-
|
10
|
+
# @param add_message_callback [Proc] A callback to call when a message is added to the thread
|
11
|
+
def initialize(messages: [], add_message_callback: nil)
|
11
12
|
raise ArgumentError, "messages array must only contain Langchain::Message instance(s)" unless messages.is_a?(Array) && messages.all? { |m| m.is_a?(Langchain::Messages::Base) }
|
12
13
|
|
14
|
+
@add_message_callback = add_message_callback
|
13
15
|
@messages = messages
|
14
16
|
end
|
15
17
|
|
@@ -34,6 +36,9 @@ module Langchain
|
|
34
36
|
def add_message(message)
|
35
37
|
raise ArgumentError, "message must be a Langchain::Message instance" unless message.is_a?(Langchain::Messages::Base)
|
36
38
|
|
39
|
+
# Call the callback with the message
|
40
|
+
add_message_callback.call(message) if add_message_callback # rubocop:disable Style/SafeNavigation
|
41
|
+
|
37
42
|
# Prepend the message to the thread
|
38
43
|
messages << message
|
39
44
|
end
|
data/lib/langchain/llm/azure.rb
CHANGED
@@ -33,8 +33,11 @@ module Langchain::LLM
|
|
33
33
|
)
|
34
34
|
@defaults = DEFAULTS.merge(default_options)
|
35
35
|
chat_parameters.update(
|
36
|
+
model: {default: @defaults[:chat_completion_model_name]},
|
36
37
|
logprobs: {},
|
37
38
|
top_logprobs: {},
|
39
|
+
n: {default: @defaults[:n]},
|
40
|
+
temperature: {default: @defaults[:temperature]},
|
38
41
|
user: {}
|
39
42
|
)
|
40
43
|
chat_parameters.ignore(:top_k)
|
@@ -39,7 +39,6 @@ module Langchain::LLM
|
|
39
39
|
def chat(params = {})
|
40
40
|
params[:system] = {parts: [{text: params[:system]}]} if params[:system]
|
41
41
|
params[:tools] = {function_declarations: params[:tools]} if params[:tools]
|
42
|
-
params[:tool_choice] = {function_calling_config: {mode: params[:tool_choice].upcase}} if params[:tool_choice]
|
43
42
|
|
44
43
|
raise ArgumentError.new("messages argument is required") if Array(params[:messages]).empty?
|
45
44
|
|
@@ -8,7 +8,7 @@ module Langchain::LLM
|
|
8
8
|
# llm = Langchain::LLM::MistralAI.new(api_key: ENV["MISTRAL_AI_API_KEY"])
|
9
9
|
class MistralAI < Base
|
10
10
|
DEFAULTS = {
|
11
|
-
chat_completion_model_name: "mistral-
|
11
|
+
chat_completion_model_name: "mistral-large-latest",
|
12
12
|
embeddings_model_name: "mistral-embed"
|
13
13
|
}.freeze
|
14
14
|
|
data/lib/langchain/llm/ollama.rb
CHANGED
@@ -218,8 +218,8 @@ module Langchain::LLM
|
|
218
218
|
top_p: nil
|
219
219
|
)
|
220
220
|
parameters = {
|
221
|
-
|
222
|
-
|
221
|
+
model: model,
|
222
|
+
input: Array(text)
|
223
223
|
}.compact
|
224
224
|
|
225
225
|
llm_parameters = {
|
@@ -243,7 +243,7 @@ module Langchain::LLM
|
|
243
243
|
|
244
244
|
parameters[:options] = llm_parameters.compact
|
245
245
|
|
246
|
-
response = client.post("api/
|
246
|
+
response = client.post("api/embed") do |req|
|
247
247
|
req.body = parameters
|
248
248
|
end
|
249
249
|
|
@@ -7,7 +7,15 @@ module Langchain::LLM
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def chat_completion
|
10
|
-
|
10
|
+
chat_completions.dig(0, "message", "content")
|
11
|
+
end
|
12
|
+
|
13
|
+
def chat_completions
|
14
|
+
raw_response.dig("choices")
|
15
|
+
end
|
16
|
+
|
17
|
+
def tool_calls
|
18
|
+
chat_completions.dig(0, "message", "tool_calls") || []
|
11
19
|
end
|
12
20
|
|
13
21
|
def role
|
@@ -58,8 +58,9 @@ module Langchain::Prompt
|
|
58
58
|
#
|
59
59
|
def format(**kwargs)
|
60
60
|
result = @template
|
61
|
+
result = result.gsub(/{{/, "{").gsub(/}}/, "}")
|
61
62
|
kwargs.each { |key, value| result = result.gsub(/\{#{key}\}/, value.to_s) }
|
62
|
-
result
|
63
|
+
result
|
63
64
|
end
|
64
65
|
|
65
66
|
#
|
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Langchain::Tool
|
2
4
|
#
|
3
|
-
# Connects to a database, executes SQL queries, and outputs DB schema for Agents to use
|
5
|
+
# Connects to a SQL database, executes SQL queries, and outputs DB schema for Agents to use
|
4
6
|
#
|
5
7
|
# Gem requirements:
|
6
8
|
# gem "sequel", "~> 5.68.0"
|
@@ -15,7 +17,9 @@ module Langchain::Tool
|
|
15
17
|
define_function :list_tables, description: "Database Tool: Returns a list of tables in the database"
|
16
18
|
|
17
19
|
define_function :describe_tables, description: "Database Tool: Returns the schema for a list of tables" do
|
18
|
-
property :tables, type: "
|
20
|
+
property :tables, type: "array", description: "The tables to describe", required: true do
|
21
|
+
item type: "string"
|
22
|
+
end
|
19
23
|
end
|
20
24
|
|
21
25
|
define_function :dump_schema, description: "Database Tool: Returns the database schema"
|
@@ -38,25 +42,32 @@ module Langchain::Tool
|
|
38
42
|
raise StandardError, "connection_string parameter cannot be blank" if connection_string.empty?
|
39
43
|
|
40
44
|
@db = Sequel.connect(connection_string)
|
45
|
+
# TODO: This is a bug, these 2 parameters are completely ignored.
|
41
46
|
@requested_tables = tables
|
42
47
|
@excluded_tables = exclude_tables
|
43
48
|
end
|
44
49
|
|
45
50
|
# Database Tool: Returns a list of tables in the database
|
51
|
+
#
|
52
|
+
# @return [Array<Symbol>] List of tables in the database
|
46
53
|
def list_tables
|
47
54
|
db.tables
|
48
55
|
end
|
49
56
|
|
50
57
|
# Database Tool: Returns the schema for a list of tables
|
51
58
|
#
|
52
|
-
# @param tables [String] The tables to describe.
|
53
|
-
# @return [String]
|
54
|
-
def describe_tables(tables:)
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
59
|
+
# @param tables [Array<String>] The tables to describe.
|
60
|
+
# @return [String] The schema for the tables
|
61
|
+
def describe_tables(tables: [])
|
62
|
+
return "No tables specified" if tables.empty?
|
63
|
+
|
64
|
+
Langchain.logger.info("Describing tables: #{tables}", for: self.class)
|
65
|
+
|
66
|
+
tables
|
67
|
+
.map do |table|
|
68
|
+
describe_table(table)
|
69
|
+
end
|
70
|
+
.join("\n")
|
60
71
|
end
|
61
72
|
|
62
73
|
# Database Tool: Returns the database schema
|
@@ -64,18 +75,39 @@ module Langchain::Tool
|
|
64
75
|
# @return [String] Database schema
|
65
76
|
def dump_schema
|
66
77
|
Langchain.logger.info("Dumping schema tables and keys", for: self.class)
|
67
|
-
|
68
|
-
db.tables.
|
69
|
-
describe_table(table
|
78
|
+
|
79
|
+
schemas = db.tables.map do |table|
|
80
|
+
describe_table(table)
|
70
81
|
end
|
71
|
-
|
82
|
+
schemas.join("\n")
|
72
83
|
end
|
73
84
|
|
74
|
-
|
85
|
+
# Database Tool: Executes a SQL query and returns the results
|
86
|
+
#
|
87
|
+
# @param input [String] SQL query to be executed
|
88
|
+
# @return [Array] Results from the SQL query
|
89
|
+
def execute(input:)
|
90
|
+
Langchain.logger.info("Executing \"#{input}\"", for: self.class)
|
91
|
+
|
92
|
+
db[input].to_a
|
93
|
+
rescue Sequel::DatabaseError => e
|
94
|
+
Langchain.logger.error(e.message, for: self.class)
|
95
|
+
e.message # Return error to LLM
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
# Describes a table and its schema
|
101
|
+
#
|
102
|
+
# @param table [String] The table to describe
|
103
|
+
# @return [String] The schema for the table
|
104
|
+
def describe_table(table)
|
105
|
+
# TODO: There's probably a clear way to do all of this below
|
106
|
+
|
75
107
|
primary_key_columns = []
|
76
108
|
primary_key_column_count = db.schema(table).count { |column| column[1][:primary_key] == true }
|
77
109
|
|
78
|
-
schema
|
110
|
+
schema = "CREATE TABLE #{table}(\n"
|
79
111
|
db.schema(table).each do |column|
|
80
112
|
schema << "#{column[0]} #{column[1][:type]}"
|
81
113
|
if column[1][:primary_key] == true
|
@@ -95,17 +127,5 @@ module Langchain::Tool
|
|
95
127
|
end
|
96
128
|
schema << ");\n"
|
97
129
|
end
|
98
|
-
|
99
|
-
# Database Tool: Executes a SQL query and returns the results
|
100
|
-
#
|
101
|
-
# @param input [String] SQL query to be executed
|
102
|
-
# @return [Array] Results from the SQL query
|
103
|
-
def execute(input:)
|
104
|
-
Langchain.logger.info("Executing \"#{input}\"", for: self.class)
|
105
|
-
|
106
|
-
db[input].to_a
|
107
|
-
rescue Sequel::DatabaseError => e
|
108
|
-
Langchain.logger.error(e.message, for: self.class)
|
109
|
-
end
|
110
130
|
end
|
111
131
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "json"
|
4
|
-
|
5
3
|
#
|
6
4
|
# Extends a class to be used as a tool in the assistant.
|
7
5
|
# A tool is a collection of functions (methods) used to perform specific tasks.
|
@@ -42,8 +40,8 @@ module Langchain::ToolDefinition
|
|
42
40
|
# @param method_name [Symbol] Name of the method to define
|
43
41
|
# @param description [String] Description of the function
|
44
42
|
# @yield Block that defines the parameters for the function
|
45
|
-
def define_function(method_name, description:, &)
|
46
|
-
function_schemas.add_function(method_name:, description:, &)
|
43
|
+
def define_function(method_name, description:, &block)
|
44
|
+
function_schemas.add_function(method_name:, description:, &block)
|
47
45
|
end
|
48
46
|
|
49
47
|
# Returns the FunctionSchemas instance for this tool
|
@@ -76,11 +74,11 @@ module Langchain::ToolDefinition
|
|
76
74
|
# @param description [String] Description of the function
|
77
75
|
# @yield Block that defines the parameters for the function
|
78
76
|
# @raise [ArgumentError] If a block is defined and no parameters are specified for the function
|
79
|
-
def add_function(method_name:, description:, &)
|
77
|
+
def add_function(method_name:, description:, &block)
|
80
78
|
name = "#{@tool_name}__#{method_name}"
|
81
79
|
|
82
|
-
if block_given?
|
83
|
-
parameters = ParameterBuilder.new(parent_type: "object").build(&)
|
80
|
+
if block_given? # rubocop:disable Performance/BlockGivenWithExplicitBlock
|
81
|
+
parameters = ParameterBuilder.new(parent_type: "object").build(&block)
|
84
82
|
|
85
83
|
if parameters[:properties].empty?
|
86
84
|
raise ArgumentError, "Function parameters must have at least one property defined within it, if a block is provided"
|
@@ -130,8 +128,8 @@ module Langchain::ToolDefinition
|
|
130
128
|
#
|
131
129
|
# @yield Block that defines the properties of the schema
|
132
130
|
# @return [Hash] The built schema
|
133
|
-
def build(&)
|
134
|
-
instance_eval(&)
|
131
|
+
def build(&block)
|
132
|
+
instance_eval(&block)
|
135
133
|
@schema
|
136
134
|
end
|
137
135
|
|
@@ -144,13 +142,13 @@ module Langchain::ToolDefinition
|
|
144
142
|
# @param required [Boolean] Whether the property is required
|
145
143
|
# @yield [Block] Block for nested properties (only for object and array types)
|
146
144
|
# @raise [ArgumentError] If any parameter is invalid
|
147
|
-
def property(name = nil, type:, description: nil, enum: nil, required: false, &)
|
145
|
+
def property(name = nil, type:, description: nil, enum: nil, required: false, &block)
|
148
146
|
validate_parameters(name:, type:, enum:, required:)
|
149
147
|
|
150
148
|
prop = {type:, description:, enum:}.compact
|
151
149
|
|
152
|
-
if block_given?
|
153
|
-
nested_schema = ParameterBuilder.new(parent_type: type).build(&)
|
150
|
+
if block_given? # rubocop:disable Performance/BlockGivenWithExplicitBlock
|
151
|
+
nested_schema = ParameterBuilder.new(parent_type: type).build(&block)
|
154
152
|
|
155
153
|
case type
|
156
154
|
when "object"
|
data/lib/langchain/version.rb
CHANGED
data/lib/langchain.rb
CHANGED
@@ -4,6 +4,9 @@ require "logger"
|
|
4
4
|
require "pathname"
|
5
5
|
require "rainbow"
|
6
6
|
require "zeitwerk"
|
7
|
+
require "uri"
|
8
|
+
require "json"
|
9
|
+
|
7
10
|
loader = Zeitwerk::Loader.for_gem
|
8
11
|
loader.ignore("#{__dir__}/langchainrb.rb")
|
9
12
|
loader.inflector.inflect(
|
@@ -18,6 +21,7 @@ loader.inflector.inflect(
|
|
18
21
|
"llm" => "LLM",
|
19
22
|
"mistral_ai" => "MistralAI",
|
20
23
|
"mistral_ai_response" => "MistralAIResponse",
|
24
|
+
"mistral_ai_message" => "MistralAIMessage",
|
21
25
|
"openai" => "OpenAI",
|
22
26
|
"openai_validator" => "OpenAIValidator",
|
23
27
|
"openai_response" => "OpenAIResponse",
|
@@ -29,6 +33,7 @@ loader.collapse("#{__dir__}/langchain/assistants")
|
|
29
33
|
|
30
34
|
loader.collapse("#{__dir__}/langchain/tool/calculator")
|
31
35
|
loader.collapse("#{__dir__}/langchain/tool/database")
|
36
|
+
loader.collapse("#{__dir__}/langchain/tool/docs_tool")
|
32
37
|
loader.collapse("#{__dir__}/langchain/tool/file_system")
|
33
38
|
loader.collapse("#{__dir__}/langchain/tool/google_search")
|
34
39
|
loader.collapse("#{__dir__}/langchain/tool/ruby_code_interpreter")
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: langchainrb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.15.
|
4
|
+
version: 0.15.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrei Bondarev
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-09-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: baran
|
@@ -669,6 +669,7 @@ files:
|
|
669
669
|
- lib/langchain/assistants/messages/anthropic_message.rb
|
670
670
|
- lib/langchain/assistants/messages/base.rb
|
671
671
|
- lib/langchain/assistants/messages/google_gemini_message.rb
|
672
|
+
- lib/langchain/assistants/messages/mistral_ai_message.rb
|
672
673
|
- lib/langchain/assistants/messages/ollama_message.rb
|
673
674
|
- lib/langchain/assistants/messages/openai_message.rb
|
674
675
|
- lib/langchain/assistants/thread.rb
|