Improving Configuration Handling, esp. for Tools
While being quite happy that the initial prototype worked within hours, its code was very prototype-y, i.e., much of its configuration was hard-coded. In a second step, we want to fix this by making our target information (the SSH connection) configurable and remove all hard-coded credentials from the code.
Big Picture
We are already using python-dotenv for some of our configuration so it makes sense to further utilize this for more configuration data. In the improved implementation, our .env
will look like this:
OPENAI_API_KEY='secret openai API key'
TARGET_HOST=192.168.121.112
TARGET_HOSTNAME='test-1'
TARGET_USERNAME='lowpriv'
TARGET_PASSWORD='trustno1'
The prototype will read this for configuration data. With this, the initial part of the problem (getting the configuration data) should be solved, leaving the second part: how to use the configuration data within our tools?
After looking into the @tool annotation for functions, this did not look like the perfect approach. Instead we opted towards subclassing BaseTool. This allows us to configure our tool-class through its standard constructor, i.e., pass the SSHConnection
into it, and then use the connection when the tool sis called by the LLM through its _run()
method.
You can find the resulting source code in this github version. Please note, that I had a bug initially (fixed here). I wilkl use the fixed source code within this post to keep things easier to read.
Let's start with our updated tool that will be configurable:
Making our Tool configurable by switching to BaseTool
You can find the full source code at within github. This change was pretty straight-forward.
Instead of writing a function, we now create a class for each tool. We have to subclass BaseTool, the parameters for our tool are now defined in a separate class which is a subclass of BaseModel
:
ssh.py: switching to BaseModel | |
---|---|
Now for the tool class:
You can see that we are now using instance variables (name
and description
) to describe the tool. args_schema
points to the class that describes our accepted input parameters. return_direct
is set to False
. If set to True
, langgraph agents will stop when the Tool stops. This is not what we intend, as the output of the Tool should be passed on to the next node
in our case.
Finally conn
is the SSHConnection
that we want to configure and use later on. Next, we set it through the class constructor:
ssh.py: the class constructor | |
---|---|
We call the superclass constructor and additionally set the conn
instance variable.
Now we can use it within the _run
method that will be called when the tool is invoked:
Again, please ignore the ugly SSH implementation code but note that we jsut return the result on line 86 as string.
Next step is wiring everything up within our prototype code.
Improving the Configuration Handling
We now have a tool that's configurable while all needed configuration is in the .env
file. Let's connect them! First, we introduce a simple helper function that receives an environmental variable or throws an error otherwise:
initial_version.py: environment variable helper | |
---|---|
Now we can use load_dotenv()
to load the variables set within .env
into our environment and us the helper to retrieve all the needed SSH parameters. With this data we can finally create our SSHConnection
. We extracted this into a separate method for readability:
Finally, we can wire everything up within our prototype:
initial_version.py: retrieving configuration data | |
---|---|
Note that we now have a configured SSH connection within conn
. When creating the tools for our LLMs, instead of passing the functions (as we did with @tool
), we now pass in the instantiated tool-classes which receive the configured SSH connection through their constructor parameters (line 33, we also added a second tool SSHTestCredentialsTool
for credential checking):
initial_version.py: Getting all configuration from the env | |
---|---|
We can also use this when configuring our initial user question template:
And that's it.