# Langchain

## AgentiPy Integrations: LangChain

AgentiPy seamlessly integrates with LangChain, empowering your AI agents to perform complex operations directly on the Solana blockchain. This integration provides **out-of-the-box, pre-configured tooling** that simplifies the development of sophisticated AI-driven decentralized applications (dApps).

This document outlines how to set up and use AgentiPy's LangChain integration to build a console-based Solana blockchain assistant.

### Key Features

* **Pre-configured Tools:** Access Solana blockchain functionalities as readily available LangChain tools.
* **Low-Code Integration:** Focus on your agent's logic, not boilerplate code for blockchain interactions.
* **AI-Powered Blockchain Operations:** Leverage Large Language Models (LLMs) to intelligently select and execute Solana functions based on natural language queries.
* **Console Application Ready:** Easily demonstrate and test blockchain operations directly from your terminal.

### Prerequisites

Before you begin, ensure you have the following:

1. **Python 3.9+:** Installed on your system.
2. **Solana Wallet Private Key:** A base58-encoded private key for a Solana wallet with some SOL (for gas fees) and potentially other tokens you wish to interact with.
   * **Important:** For development and testing, it's highly recommended to use a **devnet private key** and devnet SOL. **Never use your mainnet private key for testing or in insecure environments.**
3. **OpenAI API Key:** An API key from OpenAI to use their `gpt-4o-mini` model.

### Installation

You'll need to install AgentiPy, LangChain, OpenAI's Python client, and `python-dotenv`:

```bash
pip install agentipy langchain-openai langgraph python-dotenv
```

### Setup Environment Variables

Create a file named `.env` in the root directory of your project (where your Python script will be located). Add your private key and OpenAI API key to this file:

```dotenv
SOLANA_PRIVATE_KEY="YOUR_BASE58_ENCODED_SOLANA_PRIVATE_KEY"
OPENAI_API_KEY="YOUR_OPENAI_API_KEY"
```

Replace `"YOUR_BASE58_ENCODED_SOLANA_PRIVATE_KEY"` and `"YOUR_OPENAI_API_KEY"` with your actual keys.

### Code Explanation: Solana Blockchain Assistant

The following Python script demonstrates a console-based AI assistant powered by AgentiPy's LangChain integration. It uses `langgraph` to create an agent that can dynamically call Solana-related tools based on user input.

```python
import asyncio
import os
import json
from dotenv import load_dotenv

# AgentiPy and LangChain imports
from agentipy.agent import SolanaAgentKit
from agentipy.langchain.core import get_all_core_tools
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage
from langgraph.graph import MessagesState
from langgraph.graph import StateGraph
from langgraph.prebuilt import ToolNode
from langgraph.prebuilt import tools_condition
from langgraph.graph import START

# Load environment variables
load_dotenv()

# --- Solana Agent and LLM Setup ---
# Initialize Solana agent with your private key and desired RPC endpoint.
# 'https://api.mainnet-beta.solana.com' is used here, but 'https://api.devnet.solana.com'
# is recommended for testing.
agent = SolanaAgentKit(
    os.getenv("SOLANA_PRIVATE_KEY"),
    "https://api.mainnet-beta.solana.com"
)

# AgentiPy provides pre-built tools compatible with LangChain.
# get_all_core_tools() retrieves functions like checking balance, creating tokens, etc.
tools = [*get_all_core_tools(solana_kit=agent)]

# Initialize the Language Model (LLM).
# The llm_with_tools is a bound version of the LLM that's aware of the available tools.
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
llm_with_tools = llm.bind_tools(tools)

# Define the system message. This guides the AI's behavior and response format.
# It emphasizes blockchain operations and specific output formatting.
sys_msg = SystemMessage(
    content="""You are an AI assistant specializing in blockchain-related operations. You help users with Solana blockchain tasks such as checking wallet balances, creating tokens, retrieving token prices, and other blockchain operations.

When responding, please follow this format:

📋 OPERATION:
[Brief description of what's being done]

🔍 DETAILS:
[Relevant information and data]

💡 RESULT:
[Final outcome or response]

📝 NOTE:
[Any additional information or recommendations, if applicable]

Remember:
1. All function inputs must be formatted as a JSON string, ensuring proper escaping. Examples:
   - "{"name":"test","surname":"test"}"
   - "{"amount":100,"recipient":"abc123"}"
   - "{"token":"USDT"}"

2. Always return valid JSON objects inside strings
3. Keys must be strings, values properly formatted (numbers as integers, booleans as true/false)
4. Escape necessary characters for valid JSON
5. For non-blockchain requests, respond with:
   "I'm sorry, I can only assist with blockchain-related operations."
"""
)

# --- LangGraph Setup ---
# The assistant function defines how the LLM processes messages within the graph.
# It invokes the LLM with the system message and the current conversation history.
def assistant(state: MessagesState):
    """
    Invokes the LLM with the current state messages and system message.
    """
    return {"messages": [llm_with_tools.invoke([sys_msg] + state["messages"])]}

# Initialize LangGraph's StateGraph to build the agent's workflow.
builder = StateGraph(MessagesState)

# Add nodes to the graph:
# 'assistant': Calls the LLM to determine the next action (respond or call a tool).
# 'tools': Executes the chosen LangChain tool.
builder.add_node("assistant", assistant)
builder.add_node("tools", ToolNode(tools))

# Define the graph's flow:
# START -> 'assistant': The conversation always begins with the assistant.
builder.add_edge(START, "assistant")

# Conditional edges from 'assistant':
# 'tools_condition' from langgraph determines if the LLM wants to call a tool.
# If so, the flow goes to 'tools'; otherwise, it remains with 'assistant' to generate a final response.
builder.add_conditional_edges(
    "assistant",
    tools_condition, # Condition to check if LLM wants to use a tool
)

# 'tools' -> 'assistant': After a tool is executed, the result is fed back to the assistant
# for it to process the output and formulate a user-friendly response.
builder.add_edge("tools", "assistant")

# Compile the graph, making it ready to be invoked.
graph = builder.compile()

# --- Response Extraction Logic ---
# Helper function to parse the AI's potentially multi-part response into a clean answer.
def extract_answer(response_text: str) -> str:
    """
    Extracts the most relevant answer from the AI's response based on the
    defined format. Prioritizes '💡 RESULT:' section or JSON outputs.
    """
    try:
        lines = response_text.split("\n")
        for line in lines:
            if line.strip().startswith("{") and line.strip().endswith("}"):
                data = json.loads(line.strip())
                if data.get("status") == "success":
                    if "balance" in data:
                        return f"Balance: {data['balance']} {data.get('token', 'SOL')}"
                    return str(data)
    except json.JSONDecodeError:
        pass
    except Exception as e:
        print(f"Error parsing potential JSON: {e}")

    sections = response_text.split("\n\n")
    for section in sections:
        if section.startswith("💡 RESULT:"):
            return section.replace("💡 RESULT:", "").strip()

    for line in response_text.split("\n"):
        if line.strip() and not line.startswith(("📋", "🔍", "💡", "📝")):
            return line.strip()

    return response_text.strip()

# --- Console Application Logic ---
# The main asynchronous function for the console application.
async def main():
    """
    Main function to run the console application.
    Prompts the user for a question, processes it, and prints the response.
    """
    print("Welcome to the Solana Blockchain Assistant!")
    print("Type 'exit' to quit.")

    while True:
        question_input = input("\nYour question: ").strip()
        if question_input.lower() == 'exit':
            print("Goodbye!")
            break

        if not question_input:
            print("Please enter a question.")
            continue

        try:
            # Wrap the user's question in a HumanMessage.
            messages = [HumanMessage(content=question_input)]
            # Invoke the compiled LangGraph with the user's message.
            result = await graph.ainvoke({"messages": messages})

            # Process the response messages from the graph.
            response_text = ""
            for m in result["messages"]:
                if hasattr(m, "content") and m.content:
                    response_text += f"{m.content}\n"
                elif hasattr(m, "tool_calls") and m.tool_calls:
                    response_text += "Performing blockchain operations via tool call...\n"
                elif hasattr(m, "tool_outputs") and m.tool_outputs:
                    response_text += f"Tool Output: {m.tool_outputs}\n"

            # Extract and print the simplified answer.
            answer = extract_answer(response_text.strip())
            print(f"\nAI Assistant: {answer}")

        except Exception as e:
            print(f"\nAn error occurred: {e}")
            print("Please ensure your environment variables are correctly set and the Solana node is accessible.")

if __name__ == "__main__":
    # Essential for asyncio on Windows to prevent runtime errors.
    if os.name == "nt":
        asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

    # Run the main console application.
    asyncio.run(main())
```

### How to Run

1. **Save the code:** Save the script above as `solana_assistant.py` (or any other `.py` file).
2. **Ensure `.env` file exists:** Make sure your `.env` file is in the same directory as `solana_assistant.py` and contains your `SOLANA_PRIVATE_KEY` and `OPENAI_API_KEY`.
3. **Run from terminal:** Open your terminal or command prompt, navigate to the directory where you saved the file, and run:

   ```bash
   python solana_assistant.py
   ```

The assistant will start and prompt you for input.

### Example Usage

Once the application is running, you can type questions into the console:


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.agentipy.fun/integrations/langchain.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
