Merge pull request #13 from chatxbt/feature/swap-tokens
Browse files- .gitattributes +35 -0
- Dockerfile +16 -7
- README.md +22 -18
- docker-compose.yml +1 -1
- pip +0 -0
- src/config/nemoguardrails/config.yml +27 -0
- src/libs/rpc_client.py +1 -1
- src/tools/crypto_swap_toolkit.py +30 -39
- src/tools/user_profile_toolkit.py +3 -0
.gitattributes
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
Dockerfile
CHANGED
|
@@ -1,25 +1,34 @@
|
|
| 1 |
# Use an official Python runtime as a parent image
|
| 2 |
FROM python:3.11-slim
|
| 3 |
|
|
|
|
|
|
|
|
|
|
| 4 |
# Set the working directory in the container
|
| 5 |
WORKDIR /app
|
| 6 |
|
| 7 |
-
# Install
|
|
|
|
| 8 |
RUN apt-get update && apt-get install -y \
|
|
|
|
| 9 |
libpq-dev \
|
| 10 |
gcc \
|
| 11 |
&& rm -rf /var/lib/apt/lists/*
|
| 12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
# Copy the rest of the application code into the container
|
| 14 |
-
COPY . .
|
| 15 |
|
| 16 |
# Install any needed packages specified in requirements.txt
|
| 17 |
RUN pip install --no-cache-dir -r requirements.txt
|
| 18 |
|
| 19 |
-
#
|
| 20 |
-
|
| 21 |
-
EXPOSE 8080
|
| 22 |
|
| 23 |
# Run chainlit when the container launches
|
| 24 |
-
CMD ["python", "-m", "chainlit", "run", "chatxbt-assistant.py", "-h", "--port", "
|
| 25 |
-
|
|
|
|
| 1 |
# Use an official Python runtime as a parent image
|
| 2 |
FROM python:3.11-slim
|
| 3 |
|
| 4 |
+
# Create a user and set permissions
|
| 5 |
+
RUN useradd -m -u 1000 user
|
| 6 |
+
|
| 7 |
# Set the working directory in the container
|
| 8 |
WORKDIR /app
|
| 9 |
|
| 10 |
+
# Install system dependencies
|
| 11 |
+
USER root
|
| 12 |
RUN apt-get update && apt-get install -y \
|
| 13 |
+
python3-dev \
|
| 14 |
libpq-dev \
|
| 15 |
gcc \
|
| 16 |
&& rm -rf /var/lib/apt/lists/*
|
| 17 |
|
| 18 |
+
# Ensure .files directory exists and is writable
|
| 19 |
+
RUN mkdir -p /app/.files && chown -R user:user /app/.files
|
| 20 |
+
|
| 21 |
+
# Switch back to the non-root user
|
| 22 |
+
USER user
|
| 23 |
+
|
| 24 |
# Copy the rest of the application code into the container
|
| 25 |
+
COPY --chown=user . .
|
| 26 |
|
| 27 |
# Install any needed packages specified in requirements.txt
|
| 28 |
RUN pip install --no-cache-dir -r requirements.txt
|
| 29 |
|
| 30 |
+
# Expose port 8080 to the world outside this container
|
| 31 |
+
EXPOSE 7860
|
|
|
|
| 32 |
|
| 33 |
# Run chainlit when the container launches
|
| 34 |
+
CMD ["python", "-m", "chainlit", "run", "chatxbt-assistant.py", "-h", "--port", "7860"]
|
|
|
README.md
CHANGED
|
@@ -1,11 +1,21 @@
|
|
| 1 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
AI-powered unification and execution protocol for web3 applications
|
| 4 |
|
| 5 |
-
|
| 6 |
## Requirements
|
| 7 |
|
| 8 |
-
The list below contains all the requirements needed to have a complete developer environment to
|
| 9 |
Follow the links to install or update these tools on your machine.
|
| 10 |
|
| 11 |
1. [Python 3.12+](https://www.python.org/downloads/release/python-3120/)
|
|
@@ -13,17 +23,15 @@ Follow the links to install or update these tools on your machine.
|
|
| 13 |
3. [Pip Installer](https://pip.pypa.io/en/stable/installation/)
|
| 14 |
4. [Python Venv](https://docs.python.org/3/library/venv.html)
|
| 15 |
|
| 16 |
-
|
| 17 |
## Installation
|
| 18 |
|
| 19 |
1. Clone the repository: `git clone https://github.com/ChatXBT/chatxbt-web3-ai-assistant.git`
|
| 20 |
-
2. Get a copy of the
|
| 21 |
3. Create a virtual environment: `python -m venv env`
|
| 22 |
4. Activate the virtual environment: `source env/bin/activate`
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
|
| 28 |
## Project Overview
|
| 29 |
|
|
@@ -36,17 +44,15 @@ The project is structured as follows:
|
|
| 36 |
5. src/search_services: `a list of search services that can be used to retrieve data for the AI assistant.`
|
| 37 |
6. src/tools: `a list of tools that can be used to extend the functionality of the AI assistant.`
|
| 38 |
|
| 39 |
-
|
| 40 |
## Guides
|
| 41 |
|
| 42 |
1. Follow the development, structure and documentation patterns of the `src/tools/coin_data_toolkit.py` to build custom toolkits
|
| 43 |
2. Follow the development, structure and documentation patterns of the `src/tools/coin_data_toolkit.py` to build classes
|
| 44 |
-
3. Pay attention to the `constants` variables and `caching` decorators to optimize
|
| 45 |
4. Create new classes in the appropriate folders to maintain an organized codebase and project
|
| 46 |
5. After installing any new package run `` to update the dependency requirements list
|
| 47 |
6. Expose RPC functions for SDKs that do not support python runtime. use [DeepKit](https://deepkit.io/documentation/rpc) and [BunJS](https://bun.sh/) if possible.
|
| 48 |
|
| 49 |
-
|
| 50 |
## Git Workflow
|
| 51 |
|
| 52 |
For this project, we will be using a very simple version of the Gitflow workflow. This workflow is designed to manage the development, release, and maintenance of software projects. It provides a clear and structured approach to managing branches, releases, and feature development.
|
|
@@ -68,21 +74,19 @@ For this project, we will be using a very simple version of the Gitflow workflow
|
|
| 68 |
3. Push your changes to the remote repository: `git push origin bugfix/my-bugfix`
|
| 69 |
4. Open a pull request on GitHub and request a review from a team member.
|
| 70 |
5. Deploy the bugfix branch to a staging environment for testing.
|
| 71 |
-
6. Once testing is finished, merge the pull
|
| 72 |
7. Once the pull request is approved, merge it into the main branch.
|
| 73 |
8. Delete the bugfix branch: `git branch -d bugfix/my-bugfix`
|
| 74 |
9. Update your local main branch: `git checkout main && git pull origin main`
|
| 75 |
|
| 76 |
-
|
| 77 |
-
## Deplopyment to producton
|
| 78 |
|
| 79 |
To be finalised and added
|
| 80 |
|
| 81 |
-
|
| 82 |
## TODO
|
| 83 |
|
| 84 |
1. Add deployment instructions
|
| 85 |
2. Setup CI/CD pipelines for automatic deployment
|
| 86 |
3. Setup and integrate `LiteLLM` instance for LLM service high availability
|
| 87 |
-
4. Add RPC class for
|
| 88 |
-
5. Write enough unit
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: ChatXBT
|
| 3 |
+
emoji: 💬
|
| 4 |
+
colorFrom: purple
|
| 5 |
+
colorTo: pink
|
| 6 |
+
sdk: docker
|
| 7 |
+
sdk_version: "latest"
|
| 8 |
+
app_file: chatxbt-assistant.py
|
| 9 |
+
pinned: false
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
# ChatXBT Web3 AI Assistant Service
|
| 13 |
|
| 14 |
AI-powered unification and execution protocol for web3 applications
|
| 15 |
|
|
|
|
| 16 |
## Requirements
|
| 17 |
|
| 18 |
+
The list below contains all the requirements needed to have a complete developer environment to start working on this project.
|
| 19 |
Follow the links to install or update these tools on your machine.
|
| 20 |
|
| 21 |
1. [Python 3.12+](https://www.python.org/downloads/release/python-3120/)
|
|
|
|
| 23 |
3. [Pip Installer](https://pip.pypa.io/en/stable/installation/)
|
| 24 |
4. [Python Venv](https://docs.python.org/3/library/venv.html)
|
| 25 |
|
|
|
|
| 26 |
## Installation
|
| 27 |
|
| 28 |
1. Clone the repository: `git clone https://github.com/ChatXBT/chatxbt-web3-ai-assistant.git`
|
| 29 |
+
2. Get a copy of the `.env` file from the project manager
|
| 30 |
3. Create a virtual environment: `python -m venv env`
|
| 31 |
4. Activate the virtual environment: `source env/bin/activate`
|
| 32 |
+
5. Install the dependencies: `pip install -r requirements.txt`
|
| 33 |
+
6. Run the application: `chainlit run chatxbt-assistant.py -w`
|
| 34 |
+
7. Open the application in your browser: `http://localhost:8000`
|
|
|
|
| 35 |
|
| 36 |
## Project Overview
|
| 37 |
|
|
|
|
| 44 |
5. src/search_services: `a list of search services that can be used to retrieve data for the AI assistant.`
|
| 45 |
6. src/tools: `a list of tools that can be used to extend the functionality of the AI assistant.`
|
| 46 |
|
|
|
|
| 47 |
## Guides
|
| 48 |
|
| 49 |
1. Follow the development, structure and documentation patterns of the `src/tools/coin_data_toolkit.py` to build custom toolkits
|
| 50 |
2. Follow the development, structure and documentation patterns of the `src/tools/coin_data_toolkit.py` to build classes
|
| 51 |
+
3. Pay attention to the `constants` variables and `caching` decorators to optimize performance of class functions/methods across the project
|
| 52 |
4. Create new classes in the appropriate folders to maintain an organized codebase and project
|
| 53 |
5. After installing any new package run `` to update the dependency requirements list
|
| 54 |
6. Expose RPC functions for SDKs that do not support python runtime. use [DeepKit](https://deepkit.io/documentation/rpc) and [BunJS](https://bun.sh/) if possible.
|
| 55 |
|
|
|
|
| 56 |
## Git Workflow
|
| 57 |
|
| 58 |
For this project, we will be using a very simple version of the Gitflow workflow. This workflow is designed to manage the development, release, and maintenance of software projects. It provides a clear and structured approach to managing branches, releases, and feature development.
|
|
|
|
| 74 |
3. Push your changes to the remote repository: `git push origin bugfix/my-bugfix`
|
| 75 |
4. Open a pull request on GitHub and request a review from a team member.
|
| 76 |
5. Deploy the bugfix branch to a staging environment for testing.
|
| 77 |
+
6. Once testing is finished, merge the pull request into the main branch.
|
| 78 |
7. Once the pull request is approved, merge it into the main branch.
|
| 79 |
8. Delete the bugfix branch: `git branch -d bugfix/my-bugfix`
|
| 80 |
9. Update your local main branch: `git checkout main && git pull origin main`
|
| 81 |
|
| 82 |
+
## Deployment to production
|
|
|
|
| 83 |
|
| 84 |
To be finalised and added
|
| 85 |
|
|
|
|
| 86 |
## TODO
|
| 87 |
|
| 88 |
1. Add deployment instructions
|
| 89 |
2. Setup CI/CD pipelines for automatic deployment
|
| 90 |
3. Setup and integrate `LiteLLM` instance for LLM service high availability
|
| 91 |
+
4. Add RPC class for remote method calls to other ChatXBT services written in other languages such as `JS`, `TS` & `GO`
|
| 92 |
+
5. Write enough unit tests to ensure class and function input and output correctness
|
docker-compose.yml
CHANGED
|
@@ -3,7 +3,7 @@ services:
|
|
| 3 |
image: chatxbt-web3-ai-assistant
|
| 4 |
build: .
|
| 5 |
ports:
|
| 6 |
-
- "
|
| 7 |
env_file:
|
| 8 |
- .env
|
| 9 |
depends_on:
|
|
|
|
| 3 |
image: chatxbt-web3-ai-assistant
|
| 4 |
build: .
|
| 5 |
ports:
|
| 6 |
+
- "7860:7860"
|
| 7 |
env_file:
|
| 8 |
- .env
|
| 9 |
depends_on:
|
pip
ADDED
|
File without changes
|
src/config/nemoguardrails/config.yml
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# config.yml
|
| 2 |
+
models:
|
| 3 |
+
- type: main
|
| 4 |
+
engine: openai
|
| 5 |
+
model: gpt-3.5-turbo-instruct
|
| 6 |
+
|
| 7 |
+
rails:
|
| 8 |
+
# Input rails are invoked when new input from the user is received.
|
| 9 |
+
input:
|
| 10 |
+
flows:
|
| 11 |
+
- check jailbreak
|
| 12 |
+
- mask sensitive data on input
|
| 13 |
+
|
| 14 |
+
# Output rails are triggered after a bot message has been generated.
|
| 15 |
+
output:
|
| 16 |
+
flows:
|
| 17 |
+
- self check facts
|
| 18 |
+
- self check hallucination
|
| 19 |
+
- activefence moderation
|
| 20 |
+
|
| 21 |
+
config:
|
| 22 |
+
# Configure the types of entities that should be masked on user input.
|
| 23 |
+
sensitive_data_detection:
|
| 24 |
+
input:
|
| 25 |
+
entities:
|
| 26 |
+
- PERSON
|
| 27 |
+
- EMAIL_ADDRESS
|
src/libs/rpc_client.py
CHANGED
|
@@ -49,4 +49,4 @@ async def rpc_call(
|
|
| 49 |
return response.json()
|
| 50 |
except httpx.RequestError as e:
|
| 51 |
print(f"Error making RPC call: {e}")
|
| 52 |
-
|
|
|
|
| 49 |
return response.json()
|
| 50 |
except httpx.RequestError as e:
|
| 51 |
print(f"Error making RPC call: {e}")
|
| 52 |
+
raise ValueError(f"Error making RPC call: {e}")
|
src/tools/crypto_swap_toolkit.py
CHANGED
|
@@ -99,58 +99,49 @@ class CryptoSwapTools(Toolkit):
|
|
| 99 |
response = asyncio.run(rpc_call(method_name="getSwapSources"))
|
| 100 |
return f"{response}"
|
| 101 |
|
| 102 |
-
def execute_swap(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
"""
|
| 104 |
Executes a swap using the 0x Swap API.
|
| 105 |
|
| 106 |
Args:
|
| 107 |
-
buy_token (str): The token to buy (e.g., 'DAI').
|
| 108 |
-
sell_token (str): The token to sell (e.g., 'ETH').
|
| 109 |
-
sell_amount (str): The amount of the sell token to swap, in the smallest unit (e.g., wei for ETH).
|
| 110 |
-
|
|
|
|
| 111 |
|
| 112 |
Returns:
|
| 113 |
dict: The transaction receipt of the swap transaction.
|
| 114 |
|
| 115 |
Example:
|
| 116 |
-
>>> execute_swap('DAI', 'ETH', '1000000000000000000', '
|
| 117 |
"""
|
| 118 |
-
# Get the swap quote
|
| 119 |
-
quote = json.loads(self.get_swap_quote(buy_token, sell_token, sell_amount))
|
| 120 |
-
logger.info(f"Swap quote: {quote}")
|
| 121 |
-
|
| 122 |
-
if 'error' in quote:
|
| 123 |
-
# return {"error": "Failed to get swap quote"}
|
| 124 |
-
return f"Error: Failed to get swap quote"
|
| 125 |
-
|
| 126 |
-
# Approve the token if needed (skip if selling ETH)
|
| 127 |
-
if sell_token != 'ETH':
|
| 128 |
-
approval_receipt = self.token_approval_helper.approve_token(sell_token, quote['allowanceTarget'],
|
| 129 |
-
sell_amount, eth_address)
|
| 130 |
-
logger.info(f"Approval receipt: {approval_receipt}")
|
| 131 |
-
|
| 132 |
-
if 'status' not in approval_receipt or approval_receipt['status'] != 1:
|
| 133 |
-
# return {"error": "Token approval failed"}
|
| 134 |
-
return f"Error: Token approval failed"
|
| 135 |
|
| 136 |
-
|
| 137 |
try:
|
| 138 |
-
|
| 139 |
-
'
|
| 140 |
-
'
|
| 141 |
-
'
|
| 142 |
-
'
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
|
|
|
|
|
|
|
|
|
| 146 |
}
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
# return receipt
|
| 152 |
-
return f"{(receipt)}"
|
| 153 |
-
except Exception as e:
|
| 154 |
logger.warning(f"Failed to execute swap: {e}")
|
| 155 |
# return {"error": str(e)}
|
| 156 |
return f"Error: {e}"
|
|
|
|
| 99 |
response = asyncio.run(rpc_call(method_name="getSwapSources"))
|
| 100 |
return f"{response}"
|
| 101 |
|
| 102 |
+
def execute_swap(
|
| 103 |
+
self,
|
| 104 |
+
buy_token: str,
|
| 105 |
+
sell_token: str,
|
| 106 |
+
sell_amount: str,
|
| 107 |
+
user_email: str,
|
| 108 |
+
chain: str = "base",
|
| 109 |
+
) -> str:
|
| 110 |
"""
|
| 111 |
Executes a swap using the 0x Swap API.
|
| 112 |
|
| 113 |
Args:
|
| 114 |
+
-buy_token (str): The token to buy (e.g., 'DAI').
|
| 115 |
+
-sell_token (str): The token to sell (e.g., 'ETH').
|
| 116 |
+
-sell_amount (str): The amount of the sell token to swap, in the smallest unit (e.g., wei for ETH).
|
| 117 |
+
- user_email (str): The email of the user for whom the wallet is being fetched.
|
| 118 |
+
- chain (str): The EVM chain for which the wallet is being fetched and used to execute this transaction. use chain base as default
|
| 119 |
|
| 120 |
Returns:
|
| 121 |
dict: The transaction receipt of the swap transaction.
|
| 122 |
|
| 123 |
Example:
|
| 124 |
+
>>> execute_swap('DAI', 'ETH', '1000000000000000000', 'userEmail', 'base')
|
| 125 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
|
| 127 |
+
logger.info("executing swap")
|
| 128 |
try:
|
| 129 |
+
params = {
|
| 130 |
+
'buyToken': buy_token,
|
| 131 |
+
'sellToken': sell_token,
|
| 132 |
+
'sellAmount': sell_amount,
|
| 133 |
+
'wallet': {
|
| 134 |
+
"userEmail": user_email,
|
| 135 |
+
"chain": chain,
|
| 136 |
+
"testnet": True,
|
| 137 |
+
"gasless": True,
|
| 138 |
+
"connected": True
|
| 139 |
+
}
|
| 140 |
}
|
| 141 |
+
response = asyncio.run(rpc_call(method_name="swapTokens", params=params))
|
| 142 |
+
logger.info(f"Swap transaction receipt: {response}")
|
| 143 |
+
return f"{response}"
|
| 144 |
+
except requests.exceptions.RequestException as e:
|
|
|
|
|
|
|
|
|
|
| 145 |
logger.warning(f"Failed to execute swap: {e}")
|
| 146 |
# return {"error": str(e)}
|
| 147 |
return f"Error: {e}"
|
src/tools/user_profile_toolkit.py
CHANGED
|
@@ -21,6 +21,9 @@ class UserProfileToolkit(Toolkit):
|
|
| 21 |
self.register(self.update_user_picture)
|
| 22 |
self.register(self.get_user_id)
|
| 23 |
|
|
|
|
|
|
|
|
|
|
| 24 |
@cl.on_chat_start
|
| 25 |
def get_user_info(self, info_type: str) -> str:
|
| 26 |
"""
|
|
|
|
| 21 |
self.register(self.update_user_picture)
|
| 22 |
self.register(self.get_user_id)
|
| 23 |
|
| 24 |
+
# fetching user email
|
| 25 |
+
self.email = self.get_user_email()
|
| 26 |
+
|
| 27 |
@cl.on_chat_start
|
| 28 |
def get_user_info(self, info_type: str) -> str:
|
| 29 |
"""
|