langchainrb 0.16.0 → 0.16.1
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/CHANGELOG.md +6 -0
- data/README.md +134 -37
- data/lib/langchain/assistants/assistant.rb +10 -8
- data/lib/langchain/llm/azure.rb +2 -1
- data/lib/langchain/llm/base.rb +4 -1
- data/lib/langchain/llm/cohere.rb +6 -1
- data/lib/langchain/llm/google_gemini.rb +16 -16
- data/lib/langchain/llm/google_palm.rb +6 -0
- data/lib/langchain/llm/google_vertex_ai.rb +19 -20
- data/lib/langchain/llm/mistral_ai.rb +3 -1
- data/lib/langchain/llm/ollama.rb +8 -7
- data/lib/langchain/llm/openai.rb +13 -8
- data/lib/langchain/prompt/loading.rb +1 -1
- data/lib/langchain/tool/calculator.rb +1 -1
- data/lib/langchain/tool/database.rb +4 -4
- data/lib/langchain/tool/google_search.rb +1 -1
- data/lib/langchain/tool/news_retriever.rb +3 -3
- data/lib/langchain/tool/ruby_code_interpreter.rb +1 -1
- data/lib/langchain/tool/weather.rb +3 -3
- data/lib/langchain/tool/wikipedia.rb +1 -1
- data/lib/langchain/vectorsearch/base.rb +0 -6
- data/lib/langchain/vectorsearch/epsilla.rb +1 -1
- data/lib/langchain/vectorsearch/hnswlib.rb +2 -2
- data/lib/langchain/version.rb +1 -1
- data/lib/langchain.rb +47 -14
- metadata +3 -19
- data/lib/langchain/contextual_logger.rb +0 -68
- data/lib/langchain/utils/colorizer.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b078089a99e9e8d6654a244165ecc9d0f3dfdd8fbc0367623d41fe771a98ac41
|
4
|
+
data.tar.gz: 890c371564ce9188087bed9eb053a59e11f7b734a44b9f753696f8458f8a7b7e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8f458bfae5af31190f41661a13c24e5cd63d5f88e594e854ee79ea3b8af1f51b20552f178c89c71e454a86e4d827b3facecd97f0fa3ef107b7b6097754fab5e3
|
7
|
+
data.tar.gz: cfe0c684f89c5eef73ceb26b70292fe8fc4f941e13795ac98bd1d3197321a1303250d2584f9e45aa530e311304004911ffe3a6af7f606f6a733baad21ff2b814
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.16.1] - 2024-09-30
|
4
|
+
- Deprecate Langchain::LLM::GooglePalm
|
5
|
+
- Allow setting response_object: {} parameter when initializing supported Langchain::LLM::* classes
|
6
|
+
- Simplify and consolidate logging for some of the LLM providers (namely OpenAI and Google). Now most of the HTTP requests are being logged when on DEBUG level
|
7
|
+
- Improve doc on how to set up a custom logger with a custom destination
|
8
|
+
|
3
9
|
## [0.16.0] - 2024-09-19
|
4
10
|
- Remove `Langchain::Thread` class as it was not needed.
|
5
11
|
- Support `cohere` provider for `Langchain::LLM::AwsBedrock#embed`
|
data/README.md
CHANGED
@@ -21,7 +21,7 @@ Available for paid consulting engagements! [Email me](mailto:andrei@sourcelabs.i
|
|
21
21
|
|
22
22
|
- [Installation](#installation)
|
23
23
|
- [Usage](#usage)
|
24
|
-
- [
|
24
|
+
- [Unified Interface for LLMs](#unified-interface-for-llms)
|
25
25
|
- [Prompt Management](#prompt-management)
|
26
26
|
- [Output Parsers](#output-parsers)
|
27
27
|
- [Building RAG](#building-retrieval-augment-generation-rag-system)
|
@@ -51,61 +51,139 @@ Additional gems may be required. They're not included by default so you can incl
|
|
51
51
|
require "langchain"
|
52
52
|
```
|
53
53
|
|
54
|
-
|
55
|
-
Langchain.rb wraps supported LLMs in a unified interface allowing you to easily swap out and test out different models.
|
54
|
+
# Unified Interface for LLMs
|
56
55
|
|
57
|
-
|
58
|
-
| LLM providers | `embed()` | `complete()` | `chat()` | `summarize()` | Notes |
|
59
|
-
| -------- |:------------------:| :-------: | :-----------------: | :-------: | :----------------- |
|
60
|
-
| [OpenAI](https://openai.com/?utm_source=langchainrb&utm_medium=github) | ✅ | ✅ | ✅ | ✅ | Including Azure OpenAI |
|
61
|
-
| [AI21](https://ai21.com/?utm_source=langchainrb&utm_medium=github) | ❌ | ✅ | ❌ | ✅ | |
|
62
|
-
| [Anthropic](https://anthropic.com/?utm_source=langchainrb&utm_medium=github) | ❌ | ✅ | ✅ | ❌ | |
|
63
|
-
| [AwsBedrock](https://aws.amazon.com/bedrock?utm_source=langchainrb&utm_medium=github) | ✅ | ✅ | ✅ | ❌ | Provides AWS, Cohere, AI21, Antropic and Stability AI models |
|
64
|
-
| [Cohere](https://cohere.com/?utm_source=langchainrb&utm_medium=github) | ✅ | ✅ | ✅ | ✅ | |
|
65
|
-
| [GooglePalm](https://ai.google/discover/palm2?utm_source=langchainrb&utm_medium=github) | ✅ | ✅ | ✅ | ✅ | |
|
66
|
-
| [GoogleVertexAI](https://cloud.google.com/vertex-ai?utm_source=langchainrb&utm_medium=github) | ✅ | ❌ | ✅ | ❌ | Requires Google Cloud service auth |
|
67
|
-
| [GoogleGemini](https://cloud.google.com/vertex-ai?utm_source=langchainrb&utm_medium=github) | ✅ | ❌ | ✅ | ❌ | Requires Gemini API Key ([get key](https://ai.google.dev/gemini-api/docs/api-key)) |
|
68
|
-
| [HuggingFace](https://huggingface.co/?utm_source=langchainrb&utm_medium=github) | ✅ | ❌ | ❌ | ❌ | |
|
69
|
-
| [MistralAI](https://mistral.ai/?utm_source=langchainrb&utm_medium=github) | ✅ | ❌ | ✅ | ❌ | |
|
70
|
-
| [Ollama](https://ollama.ai/?utm_source=langchainrb&utm_medium=github) | ✅ | ✅ | ✅ | ✅ | |
|
71
|
-
| [Replicate](https://replicate.com/?utm_source=langchainrb&utm_medium=github) | ✅ | ✅ | ✅ | ✅ | |
|
56
|
+
The `Langchain::LLM` module provides a unified interface for interacting with various Large Language Model (LLM) providers. This abstraction allows you to easily switch between different LLM backends without changing your application code.
|
72
57
|
|
58
|
+
## Supported LLM Providers
|
73
59
|
|
60
|
+
- AI21
|
61
|
+
- Anthropic
|
62
|
+
- AWS Bedrock
|
63
|
+
- Azure OpenAI
|
64
|
+
- Cohere
|
65
|
+
- Google Gemini
|
66
|
+
- Google PaLM (deprecated)
|
67
|
+
- Google Vertex AI
|
68
|
+
- HuggingFace
|
69
|
+
- LlamaCpp
|
70
|
+
- Mistral AI
|
71
|
+
- Ollama
|
72
|
+
- OpenAI
|
73
|
+
- Replicate
|
74
74
|
|
75
|
-
|
75
|
+
## Usage
|
76
76
|
|
77
|
-
|
77
|
+
All LLM classes inherit from `Langchain::LLM::Base` and provide a consistent interface for common operations:
|
78
78
|
|
79
|
-
|
79
|
+
1. Generating embeddings
|
80
|
+
2. Generating prompt completions
|
81
|
+
3. Generating chat completions
|
82
|
+
|
83
|
+
### Initialization
|
84
|
+
|
85
|
+
Most LLM classes can be initialized with an API key and optional default options:
|
80
86
|
|
81
87
|
```ruby
|
82
|
-
llm = Langchain::LLM::OpenAI.new(
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
llm = Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"], llm_options: { ... })
|
88
|
+
llm = Langchain::LLM::OpenAI.new(
|
89
|
+
api_key: ENV["OPENAI_API_KEY"],
|
90
|
+
default_options: { temperature: 0.7, chat_completion_model_name: "gpt-4o" }
|
91
|
+
)
|
87
92
|
```
|
88
93
|
|
89
|
-
|
94
|
+
### Generating Embeddings
|
95
|
+
|
96
|
+
Use the `embed` method to generate embeddings for given text:
|
97
|
+
|
90
98
|
```ruby
|
91
|
-
llm.embed(text: "
|
99
|
+
response = llm.embed(text: "Hello, world!")
|
100
|
+
embedding = response.embedding
|
92
101
|
```
|
93
102
|
|
94
|
-
|
103
|
+
#### Accepted parameters for `embed()`
|
104
|
+
|
105
|
+
- `text`: (Required) The input text to embed.
|
106
|
+
- `model`: (Optional) The model name to use or default embedding model will be used.
|
107
|
+
|
108
|
+
### Prompt completions
|
109
|
+
|
110
|
+
Use the `complete` method to generate completions for a given prompt:
|
111
|
+
|
95
112
|
```ruby
|
96
|
-
llm.
|
113
|
+
response = llm.complete(prompt: "Once upon a time")
|
114
|
+
completion = response.completion
|
97
115
|
```
|
98
116
|
|
99
|
-
|
117
|
+
#### Accepted parameters for `complete()`
|
118
|
+
|
119
|
+
- `prompt`: (Required) The input prompt for completion.
|
120
|
+
- `max_tokens`: (Optional) The maximum number of tokens to generate.
|
121
|
+
- `temperature`: (Optional) Controls randomness in generation. Higher values (e.g., 0.8) make output more random, while lower values (e.g., 0.2) make it more deterministic.
|
122
|
+
- `top_p`: (Optional) An alternative to temperature, controls diversity of generated tokens.
|
123
|
+
- `n`: (Optional) Number of completions to generate for each prompt.
|
124
|
+
- `stop`: (Optional) Sequences where the API will stop generating further tokens.
|
125
|
+
- `presence_penalty`: (Optional) Penalizes new tokens based on their presence in the text so far.
|
126
|
+
- `frequency_penalty`: (Optional) Penalizes new tokens based on their frequency in the text so far.
|
127
|
+
|
128
|
+
### Generating Chat Completions
|
129
|
+
|
130
|
+
Use the `chat` method to generate chat completions:
|
131
|
+
|
100
132
|
```ruby
|
101
|
-
|
133
|
+
messages = [
|
134
|
+
{ role: "system", content: "You are a helpful assistant." },
|
135
|
+
{ role: "user", content: "What's the weather like today?" }
|
136
|
+
]
|
137
|
+
response = llm.chat(messages: messages)
|
138
|
+
chat_completion = response.chat_completion
|
102
139
|
```
|
103
140
|
|
104
|
-
|
141
|
+
#### Accepted parameters for `chat()`
|
142
|
+
|
143
|
+
- `messages`: (Required) An array of message objects representing the conversation history.
|
144
|
+
- `model`: (Optional) The specific chat model to use.
|
145
|
+
- `temperature`: (Optional) Controls randomness in generation.
|
146
|
+
- `top_p`: (Optional) An alternative to temperature, controls diversity of generated tokens.
|
147
|
+
- `n`: (Optional) Number of chat completion choices to generate.
|
148
|
+
- `max_tokens`: (Optional) The maximum number of tokens to generate in the chat completion.
|
149
|
+
- `stop`: (Optional) Sequences where the API will stop generating further tokens.
|
150
|
+
- `presence_penalty`: (Optional) Penalizes new tokens based on their presence in the text so far.
|
151
|
+
- `frequency_penalty`: (Optional) Penalizes new tokens based on their frequency in the text so far.
|
152
|
+
- `logit_bias`: (Optional) Modifies the likelihood of specified tokens appearing in the completion.
|
153
|
+
- `user`: (Optional) A unique identifier representing your end-user.
|
154
|
+
- `tools`: (Optional) A list of tools the model may call.
|
155
|
+
- `tool_choice`: (Optional) Controls how the model calls functions.
|
156
|
+
|
157
|
+
## Switching LLM Providers
|
158
|
+
|
159
|
+
Thanks to the unified interface, you can easily switch between different LLM providers by changing the class you instantiate:
|
160
|
+
|
105
161
|
```ruby
|
106
|
-
|
162
|
+
# Using Anthropic
|
163
|
+
anthropic_llm = Langchain::LLM::Anthropic.new(api_key: ENV["ANTHROPIC_API_KEY"])
|
164
|
+
|
165
|
+
# Using Google Gemini
|
166
|
+
gemini_llm = Langchain::LLM::GoogleGemini.new(api_key: ENV["GOOGLE_GEMINI_API_KEY"])
|
167
|
+
|
168
|
+
# Using OpenAI
|
169
|
+
openai_llm = Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
|
107
170
|
```
|
108
171
|
|
172
|
+
## Response Objects
|
173
|
+
|
174
|
+
Each LLM method returns a response object that provides a consistent interface for accessing the results:
|
175
|
+
|
176
|
+
- `embedding`: Returns the embedding vector
|
177
|
+
- `completion`: Returns the generated text completion
|
178
|
+
- `chat_completion`: Returns the generated chat completion
|
179
|
+
- `tool_calls`: Returns tool calls made by the LLM
|
180
|
+
- `prompt_tokens`: Returns the number of tokens in the prompt
|
181
|
+
- `completion_tokens`: Returns the number of tokens in the completion
|
182
|
+
- `total_tokens`: Returns the total number of tokens used
|
183
|
+
|
184
|
+
> [!NOTE]
|
185
|
+
> While the core interface is consistent across providers, some LLMs may offer additional features or parameters. Consult the documentation for each LLM class to learn about provider-specific capabilities and options.
|
186
|
+
|
109
187
|
### Prompt Management
|
110
188
|
|
111
189
|
#### Prompt Templates
|
@@ -427,7 +505,19 @@ assistant.add_message_and_run!(content: "What's the latest news about AI?")
|
|
427
505
|
messages = assistant.messages
|
428
506
|
|
429
507
|
# Run the assistant with automatic tool execution
|
430
|
-
assistant.run
|
508
|
+
assistant.run(auto_tool_execution: true)
|
509
|
+
|
510
|
+
# If you want to stream the response, you can add a response handler
|
511
|
+
assistant = Langchain::Assistant.new(
|
512
|
+
llm: llm,
|
513
|
+
instructions: "You're a helpful AI assistant",
|
514
|
+
tools: [Langchain::Tool::NewsRetriever.new(api_key: ENV["NEWS_API_KEY"])]
|
515
|
+
) do |response_chunk|
|
516
|
+
# ...handle the response stream
|
517
|
+
# print(response_chunk.inspect)
|
518
|
+
end
|
519
|
+
assistant.add_message(content: "Hello")
|
520
|
+
assistant.run(auto_tool_execution: true)
|
431
521
|
```
|
432
522
|
|
433
523
|
### Configuration
|
@@ -536,11 +626,18 @@ Additional examples available: [/examples](https://github.com/andreibondarev/lan
|
|
536
626
|
|
537
627
|
## Logging
|
538
628
|
|
539
|
-
Langchain.rb uses standard
|
629
|
+
Langchain.rb uses the standard Ruby [Logger](https://ruby-doc.org/stdlib-2.4.0/libdoc/logger/rdoc/Logger.html) mechanism and defaults to same `level` value (currently `Logger::DEBUG`).
|
630
|
+
|
540
631
|
To show all log messages:
|
541
632
|
|
542
633
|
```ruby
|
543
|
-
Langchain.logger.level =
|
634
|
+
Langchain.logger.level = Logger::DEBUG
|
635
|
+
```
|
636
|
+
|
637
|
+
The logger logs to `STDOUT` by default. In order to configure the log destination (ie. log to a file) do:
|
638
|
+
|
639
|
+
```ruby
|
640
|
+
Langchain.logger = Logger.new("path/to/file", **Langchain::LOGGER_OPTIONS)
|
544
641
|
```
|
545
642
|
|
546
643
|
## Problems
|
@@ -29,7 +29,8 @@ module Langchain
|
|
29
29
|
instructions: nil,
|
30
30
|
tool_choice: "auto",
|
31
31
|
messages: [],
|
32
|
-
add_message_callback: nil
|
32
|
+
add_message_callback: nil,
|
33
|
+
&block
|
33
34
|
)
|
34
35
|
unless tools.is_a?(Array) && tools.all? { |tool| tool.class.singleton_class.included_modules.include?(Langchain::ToolDefinition) }
|
35
36
|
raise ArgumentError, "Tools must be an array of objects extending Langchain::ToolDefinition"
|
@@ -48,6 +49,7 @@ module Langchain
|
|
48
49
|
@tools = tools
|
49
50
|
self.tool_choice = tool_choice
|
50
51
|
@instructions = instructions
|
52
|
+
@block = block
|
51
53
|
@state = :ready
|
52
54
|
|
53
55
|
@total_prompt_tokens = 0
|
@@ -120,7 +122,7 @@ module Langchain
|
|
120
122
|
# @return [Array<Langchain::Message>] The messages
|
121
123
|
def run(auto_tool_execution: false)
|
122
124
|
if messages.empty?
|
123
|
-
Langchain.logger.warn("No messages to process")
|
125
|
+
Langchain.logger.warn("#{self.class} - No messages to process")
|
124
126
|
@state = :completed
|
125
127
|
return
|
126
128
|
end
|
@@ -270,7 +272,7 @@ module Langchain
|
|
270
272
|
#
|
271
273
|
# @return [Symbol] The completed state
|
272
274
|
def handle_system_message
|
273
|
-
Langchain.logger.warn("At least one user message is required after a system message")
|
275
|
+
Langchain.logger.warn("#{self.class} - At least one user message is required after a system message")
|
274
276
|
:completed
|
275
277
|
end
|
276
278
|
|
@@ -285,7 +287,7 @@ module Langchain
|
|
285
287
|
#
|
286
288
|
# @return [Symbol] The failed state
|
287
289
|
def handle_unexpected_message
|
288
|
-
Langchain.logger.error("Unexpected message role encountered: #{messages.last.standard_role}")
|
290
|
+
Langchain.logger.error("#{self.class} - Unexpected message role encountered: #{messages.last.standard_role}")
|
289
291
|
:failed
|
290
292
|
end
|
291
293
|
|
@@ -309,7 +311,7 @@ module Langchain
|
|
309
311
|
elsif response.completion # Currently only used by Ollama
|
310
312
|
:completed
|
311
313
|
else
|
312
|
-
Langchain.logger.error("LLM response does not contain tool calls, chat or completion response")
|
314
|
+
Langchain.logger.error("#{self.class} - LLM response does not contain tool calls, chat or completion response")
|
313
315
|
:failed
|
314
316
|
end
|
315
317
|
end
|
@@ -321,7 +323,7 @@ module Langchain
|
|
321
323
|
run_tools(messages.last.tool_calls)
|
322
324
|
:in_progress
|
323
325
|
rescue => e
|
324
|
-
Langchain.logger.error("Error running tools: #{e.message}; #{e.backtrace.join('\n')}")
|
326
|
+
Langchain.logger.error("#{self.class} - Error running tools: #{e.message}; #{e.backtrace.join('\n')}")
|
325
327
|
:failed
|
326
328
|
end
|
327
329
|
|
@@ -353,7 +355,7 @@ module Langchain
|
|
353
355
|
#
|
354
356
|
# @return [Langchain::LLM::BaseResponse] The LLM response object
|
355
357
|
def chat_with_llm
|
356
|
-
Langchain.logger.
|
358
|
+
Langchain.logger.debug("#{self.class} - Sending a call to #{llm.class}")
|
357
359
|
|
358
360
|
params = @llm_adapter.build_chat_params(
|
359
361
|
instructions: @instructions,
|
@@ -361,7 +363,7 @@ module Langchain
|
|
361
363
|
tools: @tools,
|
362
364
|
tool_choice: tool_choice
|
363
365
|
)
|
364
|
-
@llm.chat(**params)
|
366
|
+
@llm.chat(**params, &@block)
|
365
367
|
end
|
366
368
|
|
367
369
|
# Run the tools automatically
|
data/lib/langchain/llm/azure.rb
CHANGED
data/lib/langchain/llm/base.rb
CHANGED
@@ -24,7 +24,10 @@ module Langchain::LLM
|
|
24
24
|
include Langchain::DependencyHelper
|
25
25
|
|
26
26
|
# A client for communicating with the LLM
|
27
|
-
|
27
|
+
attr_accessor :client
|
28
|
+
|
29
|
+
# Default LLM options. Can be overridden by passing `default_options: {}` to the Langchain::LLM::* constructors.
|
30
|
+
attr_reader :defaults
|
28
31
|
|
29
32
|
# Ensuring backward compatibility after https://github.com/patterns-ai-core/langchainrb/pull/586
|
30
33
|
# TODO: Delete this method later
|
data/lib/langchain/llm/cohere.rb
CHANGED
@@ -27,7 +27,8 @@ module Langchain::LLM
|
|
27
27
|
@defaults = DEFAULTS.merge(default_options)
|
28
28
|
chat_parameters.update(
|
29
29
|
model: {default: @defaults[:chat_completion_model_name]},
|
30
|
-
temperature: {default: @defaults[:temperature]}
|
30
|
+
temperature: {default: @defaults[:temperature]},
|
31
|
+
response_format: {default: @defaults[:response_format]}
|
31
32
|
)
|
32
33
|
chat_parameters.remap(
|
33
34
|
system: :preamble,
|
@@ -97,6 +98,10 @@ module Langchain::LLM
|
|
97
98
|
|
98
99
|
parameters = chat_parameters.to_params(params)
|
99
100
|
|
101
|
+
# Cohere API requires `message:` parameter to be sent separately from `chat_history:`.
|
102
|
+
# We extract the last message from the messages param.
|
103
|
+
parameters[:message] = parameters[:chat_history].pop&.dig(:message)
|
104
|
+
|
100
105
|
response = client.chat(**parameters)
|
101
106
|
|
102
107
|
Langchain::LLM::CohereResponse.new(response)
|
@@ -59,15 +59,7 @@ module Langchain::LLM
|
|
59
59
|
|
60
60
|
uri = URI("https://generativelanguage.googleapis.com/v1beta/models/#{parameters[:model]}:generateContent?key=#{api_key}")
|
61
61
|
|
62
|
-
|
63
|
-
request.content_type = "application/json"
|
64
|
-
request.body = parameters.to_json
|
65
|
-
|
66
|
-
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
|
67
|
-
http.request(request)
|
68
|
-
end
|
69
|
-
|
70
|
-
parsed_response = JSON.parse(response.body)
|
62
|
+
parsed_response = http_post(uri, parameters)
|
71
63
|
|
72
64
|
wrapped_response = Langchain::LLM::GoogleGeminiResponse.new(parsed_response, model: parameters[:model])
|
73
65
|
|
@@ -95,17 +87,25 @@ module Langchain::LLM
|
|
95
87
|
|
96
88
|
uri = URI("https://generativelanguage.googleapis.com/v1beta/models/#{model}:embedContent?key=#{api_key}")
|
97
89
|
|
98
|
-
|
90
|
+
parsed_response = http_post(uri, params)
|
91
|
+
|
92
|
+
Langchain::LLM::GoogleGeminiResponse.new(parsed_response, model: model)
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def http_post(url, params)
|
98
|
+
http = Net::HTTP.new(url.hostname, url.port)
|
99
|
+
http.use_ssl = url.scheme == "https"
|
100
|
+
http.set_debug_output(Langchain.logger) if Langchain.logger.debug?
|
101
|
+
|
102
|
+
request = Net::HTTP::Post.new(url)
|
99
103
|
request.content_type = "application/json"
|
100
104
|
request.body = params.to_json
|
101
105
|
|
102
|
-
response =
|
103
|
-
http.request(request)
|
104
|
-
end
|
105
|
-
|
106
|
-
parsed_response = JSON.parse(response.body)
|
106
|
+
response = http.request(request)
|
107
107
|
|
108
|
-
|
108
|
+
JSON.parse(response.body)
|
109
109
|
end
|
110
110
|
end
|
111
111
|
end
|
@@ -11,6 +11,8 @@ module Langchain::LLM
|
|
11
11
|
# google_palm = Langchain::LLM::GooglePalm.new(api_key: ENV["GOOGLE_PALM_API_KEY"])
|
12
12
|
#
|
13
13
|
class GooglePalm < Base
|
14
|
+
extend Gem::Deprecate
|
15
|
+
|
14
16
|
DEFAULTS = {
|
15
17
|
temperature: 0.0,
|
16
18
|
dimensions: 768, # This is what the `embedding-gecko-001` model generates
|
@@ -25,12 +27,16 @@ module Langchain::LLM
|
|
25
27
|
|
26
28
|
attr_reader :defaults
|
27
29
|
|
30
|
+
# @deprecated Please use Langchain::LLM::GoogleGemini instead
|
31
|
+
#
|
32
|
+
# @param api_key [String] The API key for the Google PaLM API
|
28
33
|
def initialize(api_key:, default_options: {})
|
29
34
|
depends_on "google_palm_api"
|
30
35
|
|
31
36
|
@client = ::GooglePalmApi::Client.new(api_key: api_key)
|
32
37
|
@defaults = DEFAULTS.merge(default_options)
|
33
38
|
end
|
39
|
+
deprecate :initialize, "Langchain::LLM::GoogleGemini.new(api_key:)", 2024, 10
|
34
40
|
|
35
41
|
#
|
36
42
|
# Generate an embedding for a given text
|
@@ -63,16 +63,7 @@ module Langchain::LLM
|
|
63
63
|
|
64
64
|
uri = URI("#{url}#{model}:predict")
|
65
65
|
|
66
|
-
|
67
|
-
request.content_type = "application/json"
|
68
|
-
request["Authorization"] = "Bearer #{@authorizer.fetch_access_token!["access_token"]}"
|
69
|
-
request.body = params.to_json
|
70
|
-
|
71
|
-
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
|
72
|
-
http.request(request)
|
73
|
-
end
|
74
|
-
|
75
|
-
parsed_response = JSON.parse(response.body)
|
66
|
+
parsed_response = http_post(uri, params)
|
76
67
|
|
77
68
|
Langchain::LLM::GoogleGeminiResponse.new(parsed_response, model: model)
|
78
69
|
end
|
@@ -96,16 +87,7 @@ module Langchain::LLM
|
|
96
87
|
|
97
88
|
uri = URI("#{url}#{parameters[:model]}:generateContent")
|
98
89
|
|
99
|
-
|
100
|
-
request.content_type = "application/json"
|
101
|
-
request["Authorization"] = "Bearer #{@authorizer.fetch_access_token!["access_token"]}"
|
102
|
-
request.body = parameters.to_json
|
103
|
-
|
104
|
-
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
|
105
|
-
http.request(request)
|
106
|
-
end
|
107
|
-
|
108
|
-
parsed_response = JSON.parse(response.body)
|
90
|
+
parsed_response = http_post(uri, parameters)
|
109
91
|
|
110
92
|
wrapped_response = Langchain::LLM::GoogleGeminiResponse.new(parsed_response, model: parameters[:model])
|
111
93
|
|
@@ -115,5 +97,22 @@ module Langchain::LLM
|
|
115
97
|
raise StandardError.new(parsed_response)
|
116
98
|
end
|
117
99
|
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def http_post(url, params)
|
104
|
+
http = Net::HTTP.new(url.hostname, url.port)
|
105
|
+
http.use_ssl = url.scheme == "https"
|
106
|
+
http.set_debug_output(Langchain.logger) if Langchain.logger.debug?
|
107
|
+
|
108
|
+
request = Net::HTTP::Post.new(url)
|
109
|
+
request.content_type = "application/json"
|
110
|
+
request["Authorization"] = "Bearer #{@authorizer.fetch_access_token!["access_token"]}"
|
111
|
+
request.body = params.to_json
|
112
|
+
|
113
|
+
response = http.request(request)
|
114
|
+
|
115
|
+
JSON.parse(response.body)
|
116
|
+
end
|
118
117
|
end
|
119
118
|
end
|
@@ -26,7 +26,9 @@ module Langchain::LLM
|
|
26
26
|
chat_parameters.update(
|
27
27
|
model: {default: @defaults[:chat_completion_model_name]},
|
28
28
|
n: {default: @defaults[:n]},
|
29
|
-
safe_prompt: {}
|
29
|
+
safe_prompt: {},
|
30
|
+
temperature: {default: @defaults[:temperature]},
|
31
|
+
response_format: {default: @defaults[:response_format]}
|
30
32
|
)
|
31
33
|
chat_parameters.remap(seed: :random_seed)
|
32
34
|
chat_parameters.ignore(:n, :top_k)
|
data/lib/langchain/llm/ollama.rb
CHANGED
@@ -45,7 +45,8 @@ module Langchain::LLM
|
|
45
45
|
model: {default: @defaults[:chat_completion_model_name]},
|
46
46
|
temperature: {default: @defaults[:temperature]},
|
47
47
|
template: {},
|
48
|
-
stream: {default: false}
|
48
|
+
stream: {default: false},
|
49
|
+
response_format: {default: @defaults[:response_format]}
|
49
50
|
)
|
50
51
|
chat_parameters.remap(response_format: :format)
|
51
52
|
end
|
@@ -149,7 +150,7 @@ module Langchain::LLM
|
|
149
150
|
end
|
150
151
|
end
|
151
152
|
|
152
|
-
generate_final_completion_response(responses_stream, parameters)
|
153
|
+
generate_final_completion_response(responses_stream, parameters[:model])
|
153
154
|
end
|
154
155
|
|
155
156
|
# Generate a chat completion
|
@@ -186,7 +187,7 @@ module Langchain::LLM
|
|
186
187
|
end
|
187
188
|
end
|
188
189
|
|
189
|
-
generate_final_chat_completion_response(responses_stream, parameters)
|
190
|
+
generate_final_chat_completion_response(responses_stream, parameters[:model])
|
190
191
|
end
|
191
192
|
|
192
193
|
#
|
@@ -289,20 +290,20 @@ module Langchain::LLM
|
|
289
290
|
end
|
290
291
|
end
|
291
292
|
|
292
|
-
def generate_final_completion_response(responses_stream,
|
293
|
+
def generate_final_completion_response(responses_stream, model)
|
293
294
|
final_response = responses_stream.last.merge(
|
294
295
|
"response" => responses_stream.map { |resp| resp["response"] }.join
|
295
296
|
)
|
296
297
|
|
297
|
-
OllamaResponse.new(final_response, model:
|
298
|
+
OllamaResponse.new(final_response, model: model)
|
298
299
|
end
|
299
300
|
|
300
301
|
# BUG: If streamed, this method does not currently return the tool_calls response.
|
301
|
-
def generate_final_chat_completion_response(responses_stream,
|
302
|
+
def generate_final_chat_completion_response(responses_stream, model)
|
302
303
|
final_response = responses_stream.last
|
303
304
|
final_response["message"]["content"] = responses_stream.map { |resp| resp.dig("message", "content") }.join
|
304
305
|
|
305
|
-
OllamaResponse.new(final_response, model:
|
306
|
+
OllamaResponse.new(final_response, model: model)
|
306
307
|
end
|
307
308
|
end
|
308
309
|
end
|
data/lib/langchain/llm/openai.rb
CHANGED
@@ -26,8 +26,6 @@ module Langchain::LLM
|
|
26
26
|
"text-embedding-3-small" => 1536
|
27
27
|
}.freeze
|
28
28
|
|
29
|
-
attr_reader :defaults
|
30
|
-
|
31
29
|
# Initialize an OpenAI LLM instance
|
32
30
|
#
|
33
31
|
# @param api_key [String] The API key to use
|
@@ -35,7 +33,11 @@ module Langchain::LLM
|
|
35
33
|
def initialize(api_key:, llm_options: {}, default_options: {})
|
36
34
|
depends_on "ruby-openai", req: "openai"
|
37
35
|
|
38
|
-
|
36
|
+
llm_options[:log_errors] = Langchain.logger.debug? unless llm_options.key?(:log_errors)
|
37
|
+
|
38
|
+
@client = ::OpenAI::Client.new(access_token: api_key, **llm_options) do |f|
|
39
|
+
f.response :logger, Langchain.logger, {headers: true, bodies: true, errors: true}
|
40
|
+
end
|
39
41
|
|
40
42
|
@defaults = DEFAULTS.merge(default_options)
|
41
43
|
chat_parameters.update(
|
@@ -44,7 +46,8 @@ module Langchain::LLM
|
|
44
46
|
top_logprobs: {},
|
45
47
|
n: {default: @defaults[:n]},
|
46
48
|
temperature: {default: @defaults[:temperature]},
|
47
|
-
user: {}
|
49
|
+
user: {},
|
50
|
+
response_format: {default: @defaults[:response_format]}
|
48
51
|
)
|
49
52
|
chat_parameters.ignore(:top_k)
|
50
53
|
end
|
@@ -122,11 +125,11 @@ module Langchain::LLM
|
|
122
125
|
raise ArgumentError.new("'tool_choice' is only allowed when 'tools' are specified.")
|
123
126
|
end
|
124
127
|
|
125
|
-
# TODO: Clean this part up
|
126
128
|
if block
|
127
129
|
@response_chunks = []
|
130
|
+
parameters[:stream_options] = {include_usage: true}
|
128
131
|
parameters[:stream] = proc do |chunk, _bytesize|
|
129
|
-
chunk_content = chunk.dig("choices", 0)
|
132
|
+
chunk_content = chunk.dig("choices", 0) || {}
|
130
133
|
@response_chunks << chunk
|
131
134
|
yield chunk_content
|
132
135
|
end
|
@@ -177,7 +180,9 @@ module Langchain::LLM
|
|
177
180
|
end
|
178
181
|
|
179
182
|
def response_from_chunks
|
180
|
-
grouped_chunks = @response_chunks
|
183
|
+
grouped_chunks = @response_chunks
|
184
|
+
.group_by { |chunk| chunk.dig("choices", 0, "index") }
|
185
|
+
.except(nil) # the last chunk (that contains the token usage) has no index
|
181
186
|
final_choices = grouped_chunks.map do |index, chunks|
|
182
187
|
{
|
183
188
|
"index" => index,
|
@@ -189,7 +194,7 @@ module Langchain::LLM
|
|
189
194
|
"finish_reason" => chunks.last.dig("choices", 0, "finish_reason")
|
190
195
|
}
|
191
196
|
end
|
192
|
-
@response_chunks.first&.slice("id", "object", "created", "model")&.merge({"choices" => final_choices})
|
197
|
+
@response_chunks.first&.slice("id", "object", "created", "model")&.merge({"choices" => final_choices, "usage" => @response_chunks.last["usage"]})
|
193
198
|
end
|
194
199
|
|
195
200
|
def tool_calls_from_choice_chunks(choice_chunks)
|
@@ -79,7 +79,7 @@ module Langchain::Prompt
|
|
79
79
|
def load_from_config(config)
|
80
80
|
# If `_type` key is not present in the configuration hash, add it with a default value of `prompt`
|
81
81
|
unless config.key?("_type")
|
82
|
-
Langchain.logger.warn
|
82
|
+
Langchain.logger.warn("#{self.class} - No `_type` key found, defaulting to `prompt`")
|
83
83
|
config["_type"] = "prompt"
|
84
84
|
end
|
85
85
|
|
@@ -28,7 +28,7 @@ module Langchain::Tool
|
|
28
28
|
# @param input [String] math expression
|
29
29
|
# @return [String] Answer
|
30
30
|
def execute(input:)
|
31
|
-
Langchain.logger.
|
31
|
+
Langchain.logger.debug("#{self.class} - Executing \"#{input}\"")
|
32
32
|
|
33
33
|
Eqn::Calculator.calc(input)
|
34
34
|
rescue Eqn::ParseError, Eqn::NoVariableValueError
|
@@ -61,7 +61,7 @@ module Langchain::Tool
|
|
61
61
|
def describe_tables(tables: [])
|
62
62
|
return "No tables specified" if tables.empty?
|
63
63
|
|
64
|
-
Langchain.logger.
|
64
|
+
Langchain.logger.debug("#{self.class} - Describing tables: #{tables}")
|
65
65
|
|
66
66
|
tables
|
67
67
|
.map do |table|
|
@@ -74,7 +74,7 @@ module Langchain::Tool
|
|
74
74
|
#
|
75
75
|
# @return [String] Database schema
|
76
76
|
def dump_schema
|
77
|
-
Langchain.logger.
|
77
|
+
Langchain.logger.debug("#{self.class} - Dumping schema tables and keys")
|
78
78
|
|
79
79
|
schemas = db.tables.map do |table|
|
80
80
|
describe_table(table)
|
@@ -87,11 +87,11 @@ module Langchain::Tool
|
|
87
87
|
# @param input [String] SQL query to be executed
|
88
88
|
# @return [Array] Results from the SQL query
|
89
89
|
def execute(input:)
|
90
|
-
Langchain.logger.
|
90
|
+
Langchain.logger.debug("#{self.class} - Executing \"#{input}\"")
|
91
91
|
|
92
92
|
db[input].to_a
|
93
93
|
rescue Sequel::DatabaseError => e
|
94
|
-
Langchain.logger.error(
|
94
|
+
Langchain.logger.error("#{self.class} - #{e.message}")
|
95
95
|
e.message # Return error to LLM
|
96
96
|
end
|
97
97
|
|
@@ -38,7 +38,7 @@ module Langchain::Tool
|
|
38
38
|
# @param input [String] search query
|
39
39
|
# @return [String] Answer
|
40
40
|
def execute(input:)
|
41
|
-
Langchain.logger.
|
41
|
+
Langchain.logger.debug("#{self.class} - Executing \"#{input}\"")
|
42
42
|
|
43
43
|
results = execute_search(input: input)
|
44
44
|
|
@@ -71,7 +71,7 @@ module Langchain::Tool
|
|
71
71
|
page_size: 5, # The API default is 20 but that's too many.
|
72
72
|
page: nil
|
73
73
|
)
|
74
|
-
Langchain.logger.
|
74
|
+
Langchain.logger.debug("#{self.class} - Retrieving all news")
|
75
75
|
|
76
76
|
params = {apiKey: @api_key}
|
77
77
|
params[:q] = q if q
|
@@ -107,7 +107,7 @@ module Langchain::Tool
|
|
107
107
|
page_size: 5,
|
108
108
|
page: nil
|
109
109
|
)
|
110
|
-
Langchain.logger.
|
110
|
+
Langchain.logger.debug("#{self.class} - Retrieving top news headlines")
|
111
111
|
|
112
112
|
params = {apiKey: @api_key}
|
113
113
|
params[:country] = country if country
|
@@ -132,7 +132,7 @@ module Langchain::Tool
|
|
132
132
|
language: nil,
|
133
133
|
country: nil
|
134
134
|
)
|
135
|
-
Langchain.logger.
|
135
|
+
Langchain.logger.debug("#{self.class} - Retrieving news sources")
|
136
136
|
|
137
137
|
params = {apiKey: @api_key}
|
138
138
|
params[:country] = country if country
|
@@ -29,7 +29,7 @@ module Langchain::Tool
|
|
29
29
|
# @param input [String] ruby code expression
|
30
30
|
# @return [String] Answer
|
31
31
|
def execute(input:)
|
32
|
-
Langchain.logger.
|
32
|
+
Langchain.logger.debug("#{self.class} - Executing \"#{input}\"")
|
33
33
|
|
34
34
|
safe_eval(input)
|
35
35
|
end
|
@@ -44,7 +44,7 @@ module Langchain::Tool
|
|
44
44
|
def get_current_weather(city:, state_code:, country_code: nil, units: "imperial")
|
45
45
|
validate_input(city: city, state_code: state_code, country_code: country_code, units: units)
|
46
46
|
|
47
|
-
Langchain.logger.
|
47
|
+
Langchain.logger.debug("#{self.class} - get_current_weather #{{city:, state_code:, country_code:, units:}}")
|
48
48
|
|
49
49
|
fetch_current_weather(city: city, state_code: state_code, country_code: country_code, units: units)
|
50
50
|
end
|
@@ -74,9 +74,9 @@ module Langchain::Tool
|
|
74
74
|
request = Net::HTTP::Get.new(uri.request_uri)
|
75
75
|
request["Content-Type"] = "application/json"
|
76
76
|
|
77
|
-
Langchain.logger.
|
77
|
+
Langchain.logger.debug("#{self.class} - Sending request to OpenWeatherMap API #{{path: path, params: params.except(:appid)}}")
|
78
78
|
response = http.request(request)
|
79
|
-
Langchain.logger.
|
79
|
+
Langchain.logger.debug("#{self.class} - Received response from OpenWeatherMap API #{{status: response.code}}")
|
80
80
|
|
81
81
|
if response.code == "200"
|
82
82
|
JSON.parse(response.body)
|
@@ -29,7 +29,7 @@ module Langchain::Tool
|
|
29
29
|
# @param input [String] search query
|
30
30
|
# @return [String] Answer
|
31
31
|
def execute(input:)
|
32
|
-
Langchain.logger.
|
32
|
+
Langchain.logger.debug("#{self.class} - Executing \"#{input}\"")
|
33
33
|
|
34
34
|
page = ::Wikipedia.find(input)
|
35
35
|
# It would be nice to figure out a way to provide page.content but the LLM token limit is an issue
|
@@ -39,7 +39,7 @@ module Langchain::Vectorsearch
|
|
39
39
|
# This behavior is changed in https://github.com/epsilla-cloud/vectordb/pull/95
|
40
40
|
# Old behavior (HTTP 500) is preserved for backwards compatibility.
|
41
41
|
# It does not prevent us from using the db.
|
42
|
-
Langchain.logger.
|
42
|
+
Langchain.logger.debug("#{self.class} - Database already loaded")
|
43
43
|
else
|
44
44
|
raise "Failed to load database: #{response}"
|
45
45
|
end
|
@@ -114,12 +114,12 @@ module Langchain::Vectorsearch
|
|
114
114
|
if File.exist?(path_to_index)
|
115
115
|
client.load_index(path_to_index)
|
116
116
|
|
117
|
-
Langchain.logger.
|
117
|
+
Langchain.logger.debug("#{self.class} - Successfully loaded the index at \"#{path_to_index}\"")
|
118
118
|
else
|
119
119
|
# Default max_elements: 100, but we constantly resize the index as new data is written to it
|
120
120
|
client.init_index(max_elements: 100)
|
121
121
|
|
122
|
-
Langchain.logger.
|
122
|
+
Langchain.logger.debug("#{self.class} - Creating a new index at \"#{path_to_index}\"")
|
123
123
|
end
|
124
124
|
end
|
125
125
|
end
|
data/lib/langchain/version.rb
CHANGED
data/lib/langchain.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require "logger"
|
4
4
|
require "pathname"
|
5
|
-
require "rainbow"
|
6
5
|
require "zeitwerk"
|
7
6
|
require "uri"
|
8
7
|
require "json"
|
@@ -92,24 +91,58 @@ loader.setup
|
|
92
91
|
# Langchain.logger.level = :info
|
93
92
|
module Langchain
|
94
93
|
class << self
|
95
|
-
# @return [
|
96
|
-
|
97
|
-
|
98
|
-
# @param logger [Logger]
|
99
|
-
# @return [ContextualLogger]
|
100
|
-
def logger=(logger)
|
101
|
-
@logger = ContextualLogger.new(logger)
|
102
|
-
end
|
103
|
-
|
94
|
+
# @return [Logger]
|
95
|
+
attr_accessor :logger
|
104
96
|
# @return [Pathname]
|
105
97
|
attr_reader :root
|
106
98
|
end
|
107
99
|
|
108
|
-
self.logger ||= ::Logger.new($stdout, level: :debug)
|
109
|
-
|
110
|
-
@root = Pathname.new(__dir__)
|
111
|
-
|
112
100
|
module Errors
|
113
101
|
class BaseError < StandardError; end
|
114
102
|
end
|
103
|
+
|
104
|
+
module Colorizer
|
105
|
+
class << self
|
106
|
+
def red(str)
|
107
|
+
"\e[31m#{str}\e[0m"
|
108
|
+
end
|
109
|
+
|
110
|
+
def green(str)
|
111
|
+
"\e[32m#{str}\e[0m"
|
112
|
+
end
|
113
|
+
|
114
|
+
def yellow(str)
|
115
|
+
"\e[33m#{str}\e[0m"
|
116
|
+
end
|
117
|
+
|
118
|
+
def blue(str)
|
119
|
+
"\e[34m#{str}\e[0m"
|
120
|
+
end
|
121
|
+
|
122
|
+
def colorize_logger_msg(msg, severity)
|
123
|
+
return msg unless msg.is_a?(String)
|
124
|
+
|
125
|
+
return red(msg) if severity.to_sym == :ERROR
|
126
|
+
return yellow(msg) if severity.to_sym == :WARN
|
127
|
+
msg
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
LOGGER_OPTIONS = {
|
133
|
+
progname: "Langchain.rb",
|
134
|
+
|
135
|
+
formatter: ->(severity, time, progname, msg) do
|
136
|
+
Logger::Formatter.new.call(
|
137
|
+
severity,
|
138
|
+
time,
|
139
|
+
"[#{progname}]",
|
140
|
+
Colorizer.colorize_logger_msg(msg, severity)
|
141
|
+
)
|
142
|
+
end
|
143
|
+
}.freeze
|
144
|
+
|
145
|
+
self.logger ||= ::Logger.new($stdout, **LOGGER_OPTIONS)
|
146
|
+
|
147
|
+
@root = Pathname.new(__dir__)
|
115
148
|
end
|
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.16.
|
4
|
+
version: 0.16.1
|
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-09-
|
11
|
+
date: 2024-09-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: baran
|
@@ -24,20 +24,6 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.1.9
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rainbow
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: 3.1.0
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: 3.1.0
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: json-schema
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -680,7 +666,6 @@ files:
|
|
680
666
|
- lib/langchain/chunker/semantic.rb
|
681
667
|
- lib/langchain/chunker/sentence.rb
|
682
668
|
- lib/langchain/chunker/text.rb
|
683
|
-
- lib/langchain/contextual_logger.rb
|
684
669
|
- lib/langchain/data.rb
|
685
670
|
- lib/langchain/dependency_helper.rb
|
686
671
|
- lib/langchain/evals/ragas/answer_relevance.rb
|
@@ -758,7 +743,6 @@ files:
|
|
758
743
|
- lib/langchain/tool/weather.rb
|
759
744
|
- lib/langchain/tool/wikipedia.rb
|
760
745
|
- lib/langchain/tool_definition.rb
|
761
|
-
- lib/langchain/utils/colorizer.rb
|
762
746
|
- lib/langchain/utils/cosine_similarity.rb
|
763
747
|
- lib/langchain/utils/hash_transformer.rb
|
764
748
|
- lib/langchain/utils/to_boolean.rb
|
@@ -799,7 +783,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
799
783
|
- !ruby/object:Gem::Version
|
800
784
|
version: '0'
|
801
785
|
requirements: []
|
802
|
-
rubygems_version: 3.5.
|
786
|
+
rubygems_version: 3.5.20
|
803
787
|
signing_key:
|
804
788
|
specification_version: 4
|
805
789
|
summary: Build LLM-backed Ruby applications with Ruby's Langchain.rb
|
@@ -1,68 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Langchain
|
4
|
-
class ContextualLogger
|
5
|
-
MESSAGE_COLOR_OPTIONS = {
|
6
|
-
debug: {
|
7
|
-
color: :white
|
8
|
-
},
|
9
|
-
error: {
|
10
|
-
color: :red
|
11
|
-
},
|
12
|
-
fatal: {
|
13
|
-
color: :red,
|
14
|
-
background: :white,
|
15
|
-
mode: :bold
|
16
|
-
},
|
17
|
-
unknown: {
|
18
|
-
color: :white
|
19
|
-
},
|
20
|
-
info: {
|
21
|
-
color: :white
|
22
|
-
},
|
23
|
-
warn: {
|
24
|
-
color: :yellow,
|
25
|
-
mode: :bold
|
26
|
-
}
|
27
|
-
}
|
28
|
-
|
29
|
-
def initialize(logger)
|
30
|
-
@logger = logger
|
31
|
-
@levels = Logger::Severity.constants.map(&:downcase)
|
32
|
-
end
|
33
|
-
|
34
|
-
def respond_to_missing?(method, include_private = false)
|
35
|
-
@logger.respond_to?(method, include_private)
|
36
|
-
end
|
37
|
-
|
38
|
-
def method_missing(method, *args, **kwargs, &block)
|
39
|
-
return @logger.send(method, *args, **kwargs, &block) unless @levels.include?(method)
|
40
|
-
|
41
|
-
for_class = kwargs.delete(:for)
|
42
|
-
for_class_name = for_class&.name
|
43
|
-
|
44
|
-
log_line_parts = []
|
45
|
-
log_line_parts << colorize("[Langchain.rb]", color: :yellow)
|
46
|
-
log_line_parts << if for_class.respond_to?(:logger_options)
|
47
|
-
colorize("[#{for_class_name}]", for_class.logger_options) + ":"
|
48
|
-
elsif for_class_name
|
49
|
-
"[#{for_class_name}]:"
|
50
|
-
end
|
51
|
-
log_line_parts << colorize(args.first, MESSAGE_COLOR_OPTIONS[method])
|
52
|
-
log_line_parts << kwargs if !!kwargs && kwargs.any?
|
53
|
-
log_line_parts << block.call if block
|
54
|
-
log_line = log_line_parts.compact.join(" ")
|
55
|
-
|
56
|
-
@logger.send(
|
57
|
-
method,
|
58
|
-
log_line
|
59
|
-
)
|
60
|
-
end
|
61
|
-
|
62
|
-
private
|
63
|
-
|
64
|
-
def colorize(line, options)
|
65
|
-
Langchain::Utils::Colorizer.colorize(line, options)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Langchain
|
4
|
-
module Utils
|
5
|
-
class Colorizer
|
6
|
-
def self.colorize(line, options)
|
7
|
-
decorated_line = Rainbow(line)
|
8
|
-
options.each_pair.each do |modifier, value|
|
9
|
-
decorated_line = if modifier == :mode
|
10
|
-
decorated_line.public_send(value)
|
11
|
-
else
|
12
|
-
decorated_line.public_send(modifier, value)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
decorated_line
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|