Wednesday, July 31, 2024

What does pandas dataframe.pop do

pandas.DataFrame.pop()

The pop() method in Pandas removes a specified column from a DataFrame and returns it as a Series object.


Key points:

Modifies the original DataFrame: Unlike some other methods, pop() changes the DataFrame in-place by removing the specified column.

Returns a Series: The removed column is returned as a Pandas Series object.

Raises KeyError: If the specified column doesn't exist, a KeyError is raised.


Python

import pandas as pd


data = {'col1': [1, 2, 3], 'col2': [4, 5, 6], 'col3': [7, 8, 9]}

df = pd.DataFrame(data)


# Remove the column 'col2' and store it in a Series

popped_column = df.pop('col2')


print(df)  # Output:

          col1  col3

0         1     7

1         2     8

2         3     9


print(popped_column)  # Output:

0    4

1    5

2    6



Important Note:

For removing multiple columns, it's generally more efficient to use the drop() method instead of multiple pop() calls.

Sunday, July 28, 2024

Fast Fourier Transform (FFT) for Feature Engineering

Fast Fourier Transform (FFT) is a powerful tool for extracting frequency-based features from time-series data. By transforming data from the time domain to the frequency domain, FFT helps identify underlying patterns and periodicities that might not be apparent in the original data.


How FFT aids in Feature Engineering:

Frequency Domain Representation:


Converts time-series data into its frequency components.

Reveals dominant frequencies present in the data.

Helps identify periodic patterns or trends.

Feature Extraction:


Extracts features like peak frequencies, amplitude, and phase information.

Creates new features that capture the frequency characteristics of the data.

Reduces dimensionality by focusing on relevant frequency bands.

Noise Reduction:


Helps filter out high-frequency noise by attenuating specific frequency components.

Improves data quality and signal-to-noise ratio.

Anomaly Detection:


Identifies unusual frequency patterns that might indicate anomalies or outliers in the data.

Example Use Cases:

Economic Data: Analyzing seasonal patterns in economic indicators.

Sensor Data: Detecting machine vibrations or anomalies in sensor readings.

Financial Data: Identifying cyclical patterns in stock prices or other financial instruments.

Audio/Image Processing: Extracting features for classification or recognition tasks.


Thursday, July 25, 2024

Google Dorking: A Double-Edged Sword

Google Dorking, also known as Google Hacking, is a technique that leverages advanced search operators to uncover information on the internet that may not be readily available through standard search queries.

How it works:

Advanced Search Operators: These are special keywords or symbols used to refine search results. Examples include intitle, inurl, filetype, site, etc.

Exploiting Search Engine Functionality: Google Dorking exploits the way search engines index and process information to find specific types of data.

Common Uses:

Vulnerability Discovery: Finding exposed files, databases, or misconfigured servers.

Information Gathering: Collecting data about individuals, organizations, or specific topics.

Digital Forensics: Locating evidence or artifacts related to cybercrimes.

Search Engine Optimization (SEO): Analyzing competitor websites or finding opportunities for keyword optimization.

Example Dorks:

intitle:index.of /etc/passwd - Searches for web pages with "index.of" in the title and "/etc/passwd" in the URL, often indicating a misconfigured web server.

filetype:doc intext:confidential - Finds confidential documents in .doc format.

site:example.com inurl:admin - Searches for pages containing "admin" in the URL within the example.com domain.

Ethical Considerations:

While Google Dorking can be a valuable tool for security researchers and ethical hackers, it's essential to use it responsibly. Misusing this technique can lead to legal and ethical issues. Always respect privacy and avoid targeting individuals or organizations without proper authorization.

Important Note: Many websites have implemented measures to protect sensitive information from being indexed by search engines. Google Dorking might not be as effective as it once was.

What is an Auto Regressive Model

Autoregressive Model (AR)

An autoregressive (AR) model is a statistical model used to predict future values of a variable based on its past values. In simpler terms, it assumes that the current value of a time series is dependent on its previous values.

Key Characteristics:

Time Series Data: Specifically designed for time-series data, where the order of observations is crucial.

Linear Regression: Mathematically, it's a linear regression model where the predictors are past values of the same variable.

Order (p): The number of past values used to predict the current value is called the order of the model (denoted as AR(p)).

Mathematical Representation:

An AR(p) model can be expressed as:


Yt = c + φ1Yt-1 + φ2Yt-2 + ... + φpYt-p + εt


Yt is the value of the variable at time t

c is a constant

φ1, φ2, ..., φp are the coefficients for the past p values

Yt-1, Yt-2, ..., Yt-p are the previous p values of the variable

εt is the error term (white noise)

AR models are widely used in various fields, including:

Economics: Predicting stock prices, GDP growth, inflation rates

Finance: Forecasting asset returns, risk management

Meteorology: Predicting weather patterns

Signal processing: Analyzing time-series signals

Limitations:

AR models are best suited for stationary time series (where the statistical properties remain constant over time). Non-stationary data might require differencing or other transformations before applying AR models.

The order of the model (p) needs to be determined carefully using techniques like AIC (Akaike Information Criterion) or BIC (Bayesian Information Criterion)

In essence, AR models provide a powerful tool for understanding and forecasting time-series data by leveraging the information contained in past observations.

references:

https://www.analyticsvidhya.com/blog/2021/10/a-comprehensive-guide-to-time-series-analysis/

Wednesday, July 24, 2024

Time Series Forecasting - Part 1

In this part, the general 

Some real-world application of TSA includes weather forecasting models, stock market predictions, signal processing, and control systems. Since TSA involves producing the set of information in a particular sequence, this makes it distinct from spatial and other analyses. We could predict the future using AR, MA, ARMA, and ARIMA models


To perform the time series analysis, we have to follow the following steps:


Collecting the data and cleaning it

Preparing Visualization with respect to time vs key feature

Observing the stationarity of the series

Developing charts to understand its nature.

Model building – AR, MA, ARMA and ARIMA

Extracting insights from prediction


With the help of “Time Series,” we can prepare numerous time-based analyses and results.


Forecasting: Predicting any value for the future.

Segmentation: Grouping similar items together.

Classification: Classifying a set of items into given classes.

Descriptive analysis: Analysis of a given dataset to find out what is there in it.

Intervention analysis: Effect of changing a given variable on the outcome.


What Are the Limitations of Time Series Analysis?


Time series has the below-mentioned limitations; we have to take care of those during our data analysis.

Similar to other models, the missing values are not supported by TSA

The data points must be linear in their relationship.

Data transformations are mandatory, so they are a little expensive.

Models mostly work on Uni-variate data.


While discussing TS data types, there are two major types – stationary and non-stationary.


Stationary: A dataset should follow the below thumb rules without having Trend, Seasonality, Cyclical, and Irregularity components of the time series.


The mean value of them should be completely constant in the data during the analysis.

The variance should be constant with respect to the time-frame

Covariance measures the relationship between two variables.

Non- Stationary: If either the mean-variance or covariance is changing with respect to time, the dataset is called non-stationary.




Converting Non-Stationary Into Stationary

Let’s discuss quickly how to convert non-stationary to stationary for effective time series modeling. There are three methods available for this conversion – detrending, differencing, and transformation.



Detrending

It involves removing the trend effects from the given dataset and showing only the differences in values from the trend. It always allows cyclical patterns to be identified.


Differencing

This is a simple transformation of the series into a new time series, which we use to remove the series dependence on time and stabilize the mean of the time series, so trend and seasonality are reduced during this transformation.


Yt= Yt – Yt-1

Yt=Value with time



Transformation

This includes three different methods they are Power Transform, Square Root, and Log Transfer. The most commonly used one is Log Transfer.


Tuesday, July 23, 2024

Openshift YAML memory and cpu configurations

 In an OpenShift configuration YAML file, the memory and cpu properties within the resources section under limits and requests define resource constraints for your pods. They specify the amount of memory and CPU a pod is allowed to use (limits) and the minimum amount guaranteed (requests) by OpenShift.

Here's a breakdown of their roles:

Memory:

limits: This defines the maximum amount of memory (in units like Mi, Gi) a pod can consume. Exceeding this limit can lead to various actions by OpenShift, such as throttling resource allocation, eviction from the node, or even pod termination.

requests: This specifies the minimum amount of memory guaranteed for the pod by OpenShift. When scheduling pods on nodes, OpenShift prioritizes pods with sufficient memory available based on their requests.

CPU:

limits: This defines the maximum amount of CPU (in units like millicores, m) a pod can utilize. Exceeding this limit can also trigger throttling or even pod termination by OpenShift.

requests: This specifies the minimum CPU resources guaranteed for the pod. OpenShift tries to schedule pods on nodes with enough CPU capacity to fulfill these requests.

Why are both limits and requests important?


Limits: They prevent pods from consuming excessive resources and impacting the overall performance of the cluster or other pods running on the same node.

Requests: They ensure your pods have the minimum resources they need to function properly and avoid resource starvation. This is crucial for predictable and reliable application behavior.

Best Practices:


Set realistic memory and CPU limits based on your application's requirements.

Don't overestimate requests to avoid underutilizing resources on nodes.

Monitor resource usage of your pods to fine-tune limits and requests for optimal performance.

By effectively utilizing memory and cpu within limits and requests sections, you can ensure efficient resource allocation and proper pod scheduling within your OpenShift environment.


Wednesday, July 17, 2024

How to switch to multiple versions of python similar to nvm in Node JS

pyenv is a good tool for this. Below are the steps to be performed. 

brew install pyenv

or

brew install pyenv-virtualenv

To list all the python versions, below can be used 

pyenv install --list

To install a new python version, below can be used

pyenv install <version>

For e.g. 

pyenv install 3.9.19

By default, the system Python version is used when you run the python command. To use a different version of Python, you can use pyenv to set the global version. To set the global Python version to a specific version, run the following command:

pyenv global <version>

pyenv global 3.9.19

In my case, it did not set the global environment to 3.9.19, 

As an alternative, created a virtual environment using the command below 

pyenv virtualenv 3.9.19 myenv2 

/Users/retheesh/.pyenv/versions/3.9.19/envs/myenv2/

Now 

Now from here, another virtual environment can be given , like this below 

cd to the folder that is required and run the belt 

/Users/retheesh/.pyenv/versions/3.9.19/envs/myenv2/bin/python -m venv venv

This creates a virtual environment with the desired python version.

references:

https://robiokidenis.medium.com/how-to-install-multiple-python-on-your-mac-d20713740a2d


Tuesday, July 16, 2024

Langchain - Customer Chatbot Sample Part 1 - Intro

The tutorial basically builds a customer support bot for an airline to help users research and make travel arrangements. 

LangGraph's interrupts and checkpointers and more complex state to organize  assistant's tools and manage a user's flight bookings, hotel reservations, car rentals, and excursions. 

Pre-requisites 

- Claude is used as LLM in this case 

- most of the tools will connect to a local sqlite database (and require no additional dependencies), 

- It also provide a general web search to the agent using Tavily.

The tutorial is split up into below sections 

1. Tools 

2. Part 1 : Zero shot agent 

3. Adding confirmation 

4. Conditional Interruption 

5. Specialized workflows 

For the tools, the tools that are made are:

1. Lookup of company policies 

2. Flight  

3. Car Rentals

4. Hotels 

5. Excursions 

6. Utilities 

Second part is about Zero shot agent (Zero-Shot Agent is a type of agent that can handle new tasks or prompts without requiring explicit programming for those specific scenarios.) In this case, to a single agent given all tools to make the whole thing quite simple at least to look at it.

The second part is around adding confirmation: In this process, any steps taken by the LLM need to be confirmed by the user to make sure the steps taken are not adversely affecting the user. 

interrupt_before is used to pause the graph and return control to the user before executing any of the tools . Next part is the conditional interrupt. In this part the tools are separated into safe and unsafe tools. 

The Specialized workflow section focuses on the primary assistant fields the user's initial queries, and the graph routes to the appropriate "expert" based on the query content.


Monday, July 15, 2024

The Basics of function calling in LLM

 llm = BedrockChat(

    model_id="anthropic.claude-v2",

    model_kwargs={"temperature": 0.1},

    region_name="us-east-1",

)

base_model = AnthropicFunctions(llm=llm)

model = base_model.bind(

    functions=[

        {

            "name": "get_current_weather",

            "description": "Get the current weather in a given location",

            "parameters": {

                "type": "object",

                "properties": {

                    "location": {

                        "type": "string",

                        "description": "The city and state, e.g. San Francisco, CA",

                    },

                    "unit": {

                        "type": "string",

                        "enum": ["celsius", "fahrenheit"],

                    },

                },

                "required": ["location"],

            },

        }

    ],

    function_call={"name": "get_current_weather"},

)

res = model.invoke("What's the weather in San Francisco?")

In this case, the method get_current_weather is already a defined function which get called from the llm layer. 

refernces:

https://github.com/langchain-ai/langchain/discussions/18541




Sunday, July 14, 2024

How agent, agent node and supervisor chain work in coordination with langgraph

In Langchain, LangGraph acts as a central platform for managing agents, agent nodes, and supervisor chains, enabling them to work together for complex workflows involving large language models (LLMs). Here's a breakdown of how these elements interact:

1. Agents:

Agents are the core components that utilize LLMs for specific tasks.

They consist of code that interacts with the LLM to perform actions based on user input or other triggers.

Agents can leverage various tools like search engines or databases for additional information.

2. Agent Nodes:

Agent nodes represent individual instances of an agent.

These nodes run on different servers or processes, enabling parallel execution of agents for improved scalability.

Each agent node can have its own configuration, allowing for customization based on specific scenarios.

3. Supervisor Chain:

The supervisor chain acts as an orchestrator that manages the execution of agents and agent nodes in a workflow.

It can utilize another LLM to make decisions about which agent or node to route tasks to based on the workflow logic and input data.

The supervisor chain can also handle error handling and recovery within the workflow.

Coordination with LangGraph:

LangGraph provides the framework for defining agents, agent nodes, and supervisor chains.

It allows developers to specify the configuration, tools used, and communication channels between these elements.

LangGraph manages the runtime execution of agents and workflows, orchestrating the interactions between them.

Benefits of this Coordination:

Scalability: By distributing agents across nodes, LangChain can handle large workloads efficiently.

Flexibility: Supervisor chains enable the creation of complex workflows with conditional decision-making.

Modularity: Agents are reusable components, allowing for composable workflows.

Centralized Management: LangGraph provides a single platform for defining and managing all components.

Example Scenario:


Imagine a LangChain application designed to process customer queries.

An agent might be responsible for understanding the user's intent from their query.

Another agent could use a search engine to find relevant information based on the intent.

A supervisor chain might determine which agent to use first and then route the response to another agent for further processing.

Overall, LangChain facilitates the creation of complex workflows involving LLMs by providing a structured approach to agent design, node management, and workflow orchestration through supervisor chains.




Basics of Function calling in OpenAI

Introduction

In an API call, you can describe functions and have the model intelligently choose to output a JSON object containing arguments to call one or many functions. The Chat Completions API does not call the function; instead, the model generates JSON that you can use to call the function in your code.


The latest models (gpt-4o, gpt-4-turbo, and gpt-3.5-turbo) have been trained to both detect when a function should to be called (depending on the input) and to respond with JSON that adheres to the function signature more closely than previous models. With this capability also comes potential risks. We strongly recommend building in user confirmation flows before taking actions that impact the world on behalf of users (sending an email, posting something online, making a purchase, etc).


Common use cases

Function calling allows you to more reliably get structured data back from the model. For example, you can:


Create assistants that answer questions by calling external APIs

e.g. define functions like send_email(to: string, body: string), or get_current_weather(location: string, unit: 'celsius' | 'fahrenheit')

Convert natural language into API calls

e.g. convert "Who are my top customers?" to get_customers(min_revenue: int, created_before: string, limit: int) and call your internal API

Extract structured data from text

e.g. define a function called extract_data(name: string, birthday: string), or sql_query(query: string)

...and much more!


The basic sequence of steps for function calling is as follows:


Call the model with the user query and a set of functions defined in the functions parameter.

The model can choose to call one or more functions; if so, the content will be a stringified JSON object adhering to your custom schema (note: the model may hallucinate parameters).

Parse the string into JSON in your code, and call your function with the provided arguments if they exist.

Call the model again by appending the function response as a new message, and let the model summarize the results back to the user.



Parallel function calling

Parallel function calling is the model's ability to perform multiple function calls together, allowing the effects and results of these function calls to be resolved in parallel. This is especially useful if functions take a long time, and reduces round trips with the API. For example, the model may call functions to get the weather in 3 different locations at the same time, which will result in a message with 3 function calls in the tool_calls array, each with an id. To respond to these function calls, add 3 new messages to the conversation, each containing the result of one function call, with a tool_call_id referencing the id from tool_calls.


Parallel function calling can be disabled by passing parallel_tool_calls: false in the request. The model will only call one function at a time when parallel function calling is disabled.


In this example, we define a single function get_current_weather. The model calls the function multiple times, and after sending the function response back to the model, we let it decide the next step. It responded with a user-facing message which was telling the user the temperature in San Francisco, Tokyo, and Paris. Depending on the query, it may choose to call a function again.

Thursday, July 11, 2024

Some notes on the Supervisor Agent based approach in langchain

 Everything is based on ChatPromptTemplate in lanchain 

- A supervisor is nothing but a chain of prompt, function to decide which agent should be next, and a JSON output parser. 

- In the supervisor chain, the prompt is made from ChatPromptTemplate.from_messages function. Which accepts the system prompt, and message place holder 

- The supervisor's prompt is given an idea that you are the coordinator and you should coordinate among the various agents that are available. To give the info on the agents available, given the array of agent names.  

- Now the prompt is given only string array of options. When selecting one of the option, how does it know which to pick?  This must be with the help of system prompt given to each of the individual agents such as researcher, coder agents. With these system prompts, internally the Langchain framework must be figuring out which agent to executed and picking up agent accordingly. 




So the most crucial pieces of code is as below 


#creation of prompt for supervisor , which is as below 


prompt = ChatPromptTemplate.from_messages(

    [

        ("system", system_prompt),

        MessagesPlaceholder(variable_name="messages"),

        (

            "system",

            "Given the conversation above, who should act next?"

            " Or should we FINISH? Select one of: {options}",

        ),

    ]

).partial(options=str(options), members=", ".join(members))



#now the next important piece is function definition 

# Using openai function calling can make output parsing easier for us

function_def = {

    "name": "route",

    "description": "Select the next role.",

    "parameters": {

        "title": "routeSchema",

        "type": "object",

        "properties": {

            "next": {

                "title": "Next",

                "anyOf": [

                    {"enum": options},

                ],

            }

        },

        "required": ["next"],

    },

}



In this, the function name is route and description is Selecting any of the role. Now with it, For any project that involves supervisor, this part can be re-used possibly

Then then next part is individual agents. 


In this case, the researcher agent is like below. 


def create_agent(llm: ChatOpenAI, tools: list, system_prompt: str):

    # Each worker node will be given a name and some tools.

    prompt = ChatPromptTemplate.from_messages(

        [

            (

                "system",

                system_prompt,

            ),

            MessagesPlaceholder(variable_name="messages"),

            MessagesPlaceholder(variable_name="agent_scratchpad"),

        ]

    )

    agent = create_openai_tools_agent(llm, tools, prompt)

    executor = AgentExecutor(agent=agent, tools=tools)

    return executor


The prompt is similar to what we have in other places. The difference here is that there is Agent and AgentExecutor created here. 

The function tools partial is used to create multiple functions instead of defining those differently. 

Now question is, how it identifies when to end and whether it got the desired data from an agent's execution. 

This is where the supervisor agent concept comes in. This is achieved by conditional node in Langgraph. The implementation is like below 


for member in members:

    # We want our workers to ALWAYS "report back" to the supervisor when done

    workflow.add_edge(member, "supervisor")

# The supervisor populates the "next" field in the graph state

# which routes to a node or finishes

conditional_map = {k: k for k in members}

conditional_map["FINISH"] = END


workflow.add_conditional_edges("supervisor", lambda x: x["next"], conditional_map)



With above, each member is always connected to the supervisor and supervisor decides where to move next. 

The conditional edge function decide which is the next. This is how the whole thing is achieved. 


Conditional edges in Langchain

workflow.add_conditional_edges("supervisor", lambda x: x["next"], conditional_map)

The variable x comes from the lambda function argument. Here's a breakdown:

workflow.add_conditional_edges: This method is used to define a conditional edge within the Langchain workflow.

"supervisor": This is the source node from where the conditional edge originates.

lambda x: x["next"]: This is a lambda function that acts as the condition for the edge.

lambda x: This part defines the function's argument. In this case, the argument is named x.

x["next"]: This expression accesses a key named "next" within the value of the argument x. It's likely that the source node ("supervisor") or the overall Langchain state provides a dictionary-like object as input to this lambda function when the conditional edge is evaluated.

Overall Flow:

When the execution reaches the "supervisor" node, the condition defined by the lambda function is evaluated.

The lambda function receives the current state of the Langchain graph (possibly a dictionary) as input and assigns it to the argument x.

The function then accesses the value associated with the key "next" within x. This value likely represents the next node to proceed to based on the current state.

The add_conditional_edges method uses the outcome of the lambda function (the value of x["next"]) to determine the target node based on the provided conditional_map (which likely maps different possible values of "next" to their corresponding target nodes).

In essence, the x in the lambda function acts as a placeholder for the current state of the Langchain graph, allowing the function to dynamically determine the next node based on the value stored under the "next" key within that state.

Additional Notes:

The specific structure of the Langchain state and the contents of the conditional_map would be necessary for a complete understanding of how the target node is ultimately chosen.

Lambda functions are a concise way to define anonymous functions within an expression. They are often used for short, single-expression logic like this condition check.


References

Gemini 

Wednesday, July 10, 2024

What is Agent scratchpad in python

This is driven by a LLMChain. The prompt in the LLMChain MUST include a variable called “agent_scratchpad” where the agent can put its intermediary work.

In the context of the LangChain framework, agent_scratchpad is a function that formats the intermediate steps of the agent's actions and observations into a string. This function is used to keep track of the agent's thoughts or actions during the execution of the program. Here is the code that supports this explanation:

The agent_scratchpad has two methods available:

_format_docs(docs): This method takes a list of documents as input and formats them into a string. Each document is wrapped in <item> and <page_content> tags, with an index attribute for the item tag.


format_agent_scratchpad(intermediate_steps): This method takes a list of intermediate steps as input. Each step is a tuple containing an action and an observation. The method concatenates the log of each action and the formatted observation into a string, which it then returns.

references:

https://github.com/langchain-ai/langchain/discussions/16311


Monday, July 8, 2024

What is AgentExecutor and create_openai_tools_agent

 This code snippet in Langchain imports two functionalities related to creating and running agents:

from langchain.agents import AgentExecutor:

This line imports the AgentExecutor class from the agents module within the langchain library.

The AgentExecutor class is used to execute and manage an agent within your Langchain application. It takes care of:

Initializing the agent with user input.

Executing the agent's logic based on the provided prompt and tools.

Handling the agent's output and potentially iterating through multiple steps.

create_openai_tools_agent:

This part imports the create_openai_tools_agent function, likely also from the agents module within langchain.

This function is specifically designed to create an agent that utilizes OpenAI tools. OpenAI tools are functionalities provided by OpenAI that allow the agent to interact with external services or perform specific tasks.

When calling create_openai_tools_agent, you typically provide additional arguments to define the agent's behavior:

LLM (Large Language Model): This specifies the LLM component to be used within the agent.

Tools: This is an array containing the OpenAI tools the agent should have access to.

Prompt: This defines the initial prompt or starting point for the agent's interaction with the user.

In essence, this code snippet equips you with the tools to create and run an agent within Langchain that leverages OpenAI capabilities. You can use the AgentExecutor to manage the agent's execution flow, and the create_openai_tools_agent function to define an agent that interacts with OpenAI tools based on your specific requirements.

What Does bind_functions do in LLM

 In Langchain, llm.bind_functions is a function used to extend the capabilities of the Large Language Model (LLM) by enabling it to interact with custom functions. Here's a detailed explanation:


Binds Functions: It takes a set of custom functions and associates them with the LLM's output.

Processes LLM Output: The LLM interacts with user input or performs some task.

Function Call: Based on the defined configuration, llm.bind_functions calls the appropriate function from the provided set with the LLM's output as input.

Benefits:

Extends LLM Functionality: This allows you to integrate custom logic and processing steps into the LLM's workflow.

Customizable Behavior: By defining your own functions, you can tailor the LLM's response or actions based on specific needs.

Modular Design: It promotes a modular design where reusable functions can be applied in different parts of the Langchain application.

Function Arguments:

llm.bind_functions typically takes two main arguments:

functions: This is an array containing the custom functions you want to bind to the LLM. Each function should be defined elsewhere in your Langchain code.

function_call (optional): This argument specifies the name of the function within the functions array that should be called on the LLM's output. If not provided, Langchain might use a default strategy (e.g., calling the first function in the array).

Example:

Python

# Define a custom function

def process_user_intent(text):

  # Your logic to analyze user intent from the text

  # ...

  return intent


# Bind the function to the LLM

supervisor_chain = (

  prompt | llm.bind_functions(functions=[process_user_intent]) | JsonOutputFunctionsParser()

)


In this example:

The prompt function presents a question to the user.

The user responds.

The LLM processes the user's response.

llm.bind_functions calls the process_user_intent function with the LLM's understanding of the user input (text).

The process_user_intent function analyzes the text to determine the user's intent and returns the intent information.

The output might be parsed by JsonOutputFunctionsParser (depending on the implementation).

In essence, llm.bind_functions acts as a bridge between the LLM and your custom logic, enabling you to create more sophisticated and interactive applications within Langchain.

references:


Tuesday, July 2, 2024

The Three different Multi Agent Patterns

 Agent Supervisor pattern

Hierarchical agent Pattern


Multi Agent Collaboration Pattern









MongoDB compass getting path collision while exporting as JSON

A "path collision" error while exporting data from MongoDB Compass as JSON indicates that there are fields in your documents that share the same name at different levels within the document hierarchy. This can cause ambiguity when exporting to a flat JSON structure.

Here are ways to resolve the path collision error and successfully export your data as JSON:

1. Deselect Conflicting Fields:

Go back to the "Select Fields" section in the Export JSON window.

Uncheck the box next to the field causing the collision in the higher-level object.

This will ensure the sub-fields under that conflicting field are still included in the export, but the higher-level field itself will be omitted if it's empty or null.

2. Select Only Conflicting Fields:

Uncheck all boxes in the "Select Fields" section.

Then, navigate to the specific sub-fields within the document that you want to export, despite the collision.

This will only include the selected sub-fields (and their values) in the exported JSON, omitting the conflicting higher-level field.

3. Use the $unset Aggregation Pipeline Stage (Advanced):

This method involves modifying the data before export using the aggregation pipeline within MongoDB Compass.

You can define an aggregation stage using the $unset operator to remove the conflicting field from the higher level in the document before exporting.

Here are some resources for further guidance:

MongoDB Compass Official Documentation: https://www.mongodb.com/docs/compass/current/documents/ (Search for "export data" or "JSON export")

Stack Overflow Discussion on Path Collision: https://www.mongodb.com/community/forums/t/path-collision-trying-to-export-collection/115939

MongoDB Aggregation Pipeline - $unset: https://www.mongodb.com/docs/manual/reference/operator/aggregation/unset/ (This page explains the $unset operator)

Choosing the Right Approach:

The best solution depends on your specific needs.

If you need all sub-fields under the conflicting field and don't care about the higher-level conflicting field itself, deselect it.

If the conflicting field is irrelevant and you only want the sub-fields, select only them.

If you need more control over data manipulation before export, consider using the aggregation pipeline with $unset.

By understanding the cause of the path collision error and using these approaches, you should be able to successfully export your MongoDB data as JSON using MongoDB Compass.

Monday, July 1, 2024

What is a partial function and why does langraph use Partial functions

from functools import partial

def multiply(a, b):

  """Multiplies two numbers."""

  return a * b


# Double a number (pre-fill argument b with 2)

double = partial(multiply, b=2)


# Use the partially applied function

result = double(5)  # result will be 10 (5 * 2)


# Another example: add 10 to a number

add_ten = partial(multiply, a=10)


result = add_ten(3)  # result will be 30 (10 * 3)


# Print the original function and the partially applied ones

print(multiply)  # Output: <function multiply at 0x...>

print(double)    # Output: <partial object at 0x...> (Shows it's a partial function)

print(add_ten)  # Output: <partial object at 0x...>



We define a function multiply that takes two arguments a and b and returns their product.

We use partial from functools to create a new function double. partial takes the original function (multiply) and a keyword argument (b=2). This pre-fills the b argument of multiply with the value 2.

Now, when we call double(5), it's equivalent to calling multiply(5, 2), resulting in 10.

We create another partial function add_ten by setting a=10 in partial. So, add_ten(3) becomes multiply(10, 3), resulting in 30.

Printing the functions shows the original multiply function and the partially applied ones (double and add_ten) as distinct objects.



Langchain likely utilizes functools.partial within its nodes for several reasons:


1. Reusability and Consistency:


Langchain graphs consist of nodes representing processing steps. These nodes might involve functions that operate on specific data or interact with external tools.

By using partial to pre-fill certain arguments in these functions, Langchain ensures consistency and reusability within the graph.

For example, a node might use a function to access a database. partial can be used to create a version of this function specifically for that node, pre-filling arguments like the database connection details. This avoids the need to repeat those details within each node that uses the function.

2. Simplifying Complex Workflows:


Langchain graphs can involve complex workflows with multiple interconnected nodes.

partial helps break down complex functions into smaller, more manageable ones by fixing specific arguments relevant to the current node's context.

This improves code readability and maintainability within the graph definition.

3. Context-Specific Function Calls:


Some Langchain nodes might interact with external tools or APIs. These interactions might require different arguments depending on the specific context or data available at that node.

partial allows Langchain to create context-specific versions of functions on the fly. This ensures the functions receive the appropriate arguments based on the current state of the graph execution.

4. Integration with Custom Functions:


Langchain allows binding custom functions to extend the LLM's (Large Language Model's) capabilities.

partial might be used when binding these custom functions to provide default values for certain arguments or to adapt them to the specific needs of the Langchain graph.

references:
Gemini 

What is functools in Python

In Python, functools is a built-in module that provides various utilities for working with functions as objects. It offers tools to manipulate functions, enhance their behavior, and create new functions from existing ones. Here are some key functionalities of functools:

1. Higher-Order Functions:

The functools module deals with higher-order functions, which are functions that:

Take other functions as arguments.

Return a new function as a result.

functools provides tools to simplify working with higher-order functions, making your code more concise and readable.

2. Common Function Utilities:

Here are some commonly used functions within functools:

@wraps decorator: This decorator is used when defining wrapper functions. It ensures the wrapper function preserves the name and docstring of the wrapped function.

partial function: This function creates a new function with a subset of its arguments pre-filled with specific values. This is useful for partial application of functions where some arguments are known beforehand.

cmp_to_key function: This function helps convert a comparison function (used with operators like < or >) into a key function usable with sorting algorithms (like sorted or min).

lru_cache function: This function implements a simple least-recently-used function cache. It stores the results of function calls based on their arguments, improving performance for repeated calls with the same arguments.

3. Advanced Function Tools:

functools also offers functions for more advanced use cases, such as:

update_wrapper: Updates the wrapper function's attributes like docstring, module, and name.

total_ordering: Creates a total ordering class decorator, ensuring consistent sorting behavior for your custom classes.

cached_property: This decorator creates a read-only property that caches its result on first access, improving performance for expensive calculations.

Overall, the functools module is a valuable tool for any Python developer who wants to work effectively with functions. It helps you write cleaner, more reusable, and efficient code by providing utilities for higher-order functions and function manipulation.

Here are some resources for further exploration:

Python functools Documentation: https://docs.python.org/3/library/functools.html (This official documentation provides detailed explanations and examples for each function in the functools module)

Real Python Tutorial on functools: https://realpython.com/lessons/functools-module/ (This tutorial offers a practical introduction to common functools functionalities with code examples)

References:

Gemini

https://docs.python.org/3/library/functools.html

Where does transformer library store the model files

 Here's how the transformers library typically stores downloaded model data:

Library: transformers

Function: AutoModelForCausalLM.from_pretrained (or similar for different model types)

Storage Location:

The downloaded model data is typically stored in a cache directory created by the transformers library. The exact location depends on your operating system and environment:

Windows: Typically in %USERPROFILE%\.cache\huggingface\hub

macOS/Linux: Usually in ~/.cache/huggingface/hub (tilde represents your home directory)

This cache directory can contain subfolders for different models you've downloaded using the transformers library. Inside each model's folder, you'll find the various files constituting the model, such as:

config.json: Configuration file defining the model architecture.

pytorch_model.bin: Weights of the model (for PyTorch models).

tf_model.h5 or saved_model.pb (for TensorFlow models).

tokenizer.json: Vocabulary file used for processing text.

Additional files depending on the specific model format.

Important Notes:

You don't usually need to directly access these files unless you're troubleshooting or performing advanced tasks.

The transformers library manages the cache location and retrieves the necessary files when you load the model using from_pretrained.

Finding the Downloaded Model:

If you want to see the location of the downloaded Llama model data, you can:

Check the transformers documentation: It might specify the default cache location.

Look for environment variables: Some environments might have variables defining the cache location.

Use your OS file explorer: Navigate to the typical cache locations mentioned above and search for folders with names matching the downloaded Llama model.

Remember, accessing and manipulating these files directly is not recommended for regular usage. Interact with the model using the transformers library functions to ensure proper functionality and avoid potential issues.