langchainrb 0.16.0 → 0.16.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|