# linedify
đŦ⥠linedify: Supercharging your LINE Bot with Dify power!
## ⨠Features
- 𧊠Seamless Dify-LINE Bot Integration
- Connect Dify with LINE Bot using minimal code
- Build powerful and efficient chatbots in no time
- đ¸ Rich Input Support
- Handle images, location data, and stickers out of the box
- Customize to work with LINE-specific UI like Flex Messages
- đĒ Developer-Friendly
- Built on FastAPI for high performance and easy scaling
- Asynchronous processing for smooth operations
## đĻ Install
```sh
pip install linedify
```
## đ Quick Start
Make the following script as `run.py` as the handler for WebHook from LINE API server.
By passing the HTTP request body and signature to `line_dify.process_request`, the entire process from receiving user messages to calling Dify and responding to the user is executed.
```python
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request, BackgroundTasks
from linedify import LineDify
# LINE Bot - Dify Agent Integrator
line_dify = LineDify(
line_channel_access_token=YOUR_CHANNEL_ACCESS_TOKEN,
line_channel_secret=YOUR_CHANNEL_SECRET,
dify_api_key=DIFY_API_KEY,
dify_base_url=DIFY_BASE_URL, # e.g. http://localhost/v1
dify_user=DIFY_USER
)
# FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
yield
await line_dify.shutdown()
app = FastAPI(lifespan=lifespan)
@app.post("/linebot")
async def handle_request(request: Request, background_tasks: BackgroundTasks):
background_tasks.add_task(
line_dify.process_request,
request_body=(await request.body()).decode("utf-8"),
signature=request.headers.get("X-Line-Signature", "")
)
return "ok"
```
Start server.
```
uvicorn run:app
```
NOTE: You have to expose the host:port to where the LINE API server can access.
## đšī¸ Switching Types
linedify supports Agent and Chatbot for now. (You can add support for TextGenerator and Workflow on your own!)
You can switch the types by setting `dify_type` to the constructor of LineDify. Default is `DifyType.Agent`.
```python
line_dify = LineDify(
line_channel_access_token=YOUR_CHANNEL_ACCESS_TOKEN,
line_channel_secret=YOUR_CHANNEL_SECRET,
dify_api_key=DIFY_API_KEY,
dify_base_url=DIFY_BASE_URL,
dify_user=DIFY_USER,
dify_type=DifyType.Chatbot # <- DifyType.Agent or DifyType.Chatbot
)
```
## đ Use UI Components
Implement function to edit reply message below the decorator `@line_dify.to_reply_message`.
```python
from typing import List
from linebot.v3.messaging import Message, TextMessage, QuickReply, QuickReplyItem, MessageAction
from linedify.session import ConversationSession
@line_dify.to_reply_message
async def to_reply_message(text: str, data: dict, session: ConversationSession) -> List[Message]:
response_message = TextMessage(text=text)
# Show QuickReply buttons when tool "reservation" was executed on Dify
if tool := data.get("tool"):
if tool == "reservation":
response_message.quick_reply = QuickReply([
QuickReplyItem(action=MessageAction(label="Checkout", text="Checkout")),
QuickReplyItem(action=MessageAction(label="Cancel", text="Cancel"))
])
return [response_message]
```
## đ¨ Custom Logic
### Event Validation
Use `@line_dify.validate_event` to validate event before handling.
```python
banned_users = ["U123456", "U234567"]
@line_dify.validate_event
async def validate_event(event):
line_user_id = event.source.user_id
if line_user_id in banned_users:
# Return the list of TextMessage to reply immediately without processing the event
return [TextMessage("Forbidden")]
```
### Handle events
Use `@line_dify.event(event_type)` to customize event handlers.
```python
# Add handler for Postback event
@line_dify.event("postback")
async def handle_message_event(event: PostbackEvent):
# Do something here
# Return reply messages
return [TextMessage(f"Response for postback event: {event.postback.data}")]
# Add handler for unspecified event
@line_dify.event()
async def handle_event(event):
# Do something here
# Return reply messages
return [TextMessage(f"Response for event type: {event.type}")]
```
### Parse messages
Use `@line_dify.parse_message(message_type)` to customize message parsers.
```python
@line_dify.parse_message("location")
async def parse_location_message(message):
text, _ = await line_dify.parse_location_message(message)
map_image = get_map_image(message.address)
return (text, map_image)
```
### Inputs
Use `@line_dify.make_inputs` to customize `inputs` as arguments for Dify conversation threads.
```python
@line_dify.make_inputs
async def make_inputs(session: ConversationSession):
# You can use session to customize inputs dynamically here
inputs = {
"line_user_id": session.user_id,
"favorite_food": "apple"
}
return inputs
```
### Error Message
Use `@line_dify.to_error_message` to customize reply message when error occurs.
```python
@line_dify.to_error_message
async def to_error_message(event: Event, ex: Exception, session: ConversationSession = None):
# Custom logic here
text = random.choice(["Error đĨ˛", "đĩ Something wrong...", "đ"])
# Return reply messages
return [TextMessage(text=text)]
```
## đž Conversation Session
Conversation sessions are managed by a database. By default, SQLite is used, but you can specify the file path or database type using `session_db_url`. For the syntax, please refer to SQLAlchemy's documentation.
Additionally, you can specify the session validity period with `session_timeout`. The default is 3600 seconds. If this period elapses since the last conversation, a new conversation thread will be created on Dify when the next conversation starts.
```python
line_dify = LineDify(
line_channel_access_token=YOUR_CHANNEL_ACCESS_TOKEN,
line_channel_secret=YOUR_CHANNEL_SECRET,
dify_api_key=DIFY_API_KEY,
dify_base_url=DIFY_BASE_URL,
dify_user=DIFY_USER,
session_db_url="sqlite:///your_sessions.db", # SQLAlchemy database url
session_timeout=1800, # Timeout in seconds
)
```
## đ Debug
Set `verbose=True` to see the request and response, both from/to LINE and from/to Dify.
```python
line_dify = LineDify(
line_channel_access_token=YOUR_CHANNEL_ACCESS_TOKEN,
line_channel_secret=YOUR_CHANNEL_SECRET,
dify_api_key=DIFY_API_KEY,
dify_base_url=DIFY_BASE_URL,
dify_user=DIFY_USER,
verbose=True
)
```
## âī¸ License
linedify is distributed under the Apache v2 license.
(c)uezo, made with big â¤ī¸ in Tokyo.
Raw data
{
"_id": null,
"home_page": "https://github.com/uezo/linedify",
"name": "linedify",
"maintainer": "uezo",
"docs_url": null,
"requires_python": null,
"maintainer_email": "uezo@uezo.net",
"keywords": null,
"author": "uezo",
"author_email": "uezo@uezo.net",
"download_url": null,
"platform": null,
"description": "# linedify\n\n\ud83d\udcac\u26a1 linedify: Supercharging your LINE Bot with Dify power!\n\n\n## \u2728 Features\n\n- \ud83e\udde9 Seamless Dify-LINE Bot Integration\n\n - Connect Dify with LINE Bot using minimal code\n - Build powerful and efficient chatbots in no time\n\n- \ud83d\udcf8 Rich Input Support\n\n - Handle images, location data, and stickers out of the box\n - Customize to work with LINE-specific UI like Flex Messages\n\n- \ud83e\ude84 Developer-Friendly\n\n - Built on FastAPI for high performance and easy scaling\n - Asynchronous processing for smooth operations\n\n\n## \ud83d\udce6 Install\n\n```sh\npip install linedify\n```\n\n\n## \ud83d\ude80 Quick Start\n\nMake the following script as `run.py` as the handler for WebHook from LINE API server.\n\nBy passing the HTTP request body and signature to `line_dify.process_request`, the entire process from receiving user messages to calling Dify and responding to the user is executed.\n\n```python\nfrom contextlib import asynccontextmanager\nfrom fastapi import FastAPI, Request, BackgroundTasks\nfrom linedify import LineDify\n\n# LINE Bot - Dify Agent Integrator\nline_dify = LineDify(\n line_channel_access_token=YOUR_CHANNEL_ACCESS_TOKEN,\n line_channel_secret=YOUR_CHANNEL_SECRET,\n dify_api_key=DIFY_API_KEY,\n dify_base_url=DIFY_BASE_URL, # e.g. http://localhost/v1\n dify_user=DIFY_USER\n)\n\n# FastAPI\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n yield\n await line_dify.shutdown()\n\napp = FastAPI(lifespan=lifespan)\n\n@app.post(\"/linebot\")\nasync def handle_request(request: Request, background_tasks: BackgroundTasks):\n background_tasks.add_task(\n line_dify.process_request,\n request_body=(await request.body()).decode(\"utf-8\"),\n signature=request.headers.get(\"X-Line-Signature\", \"\")\n )\n return \"ok\"\n```\n\nStart server.\n\n```\nuvicorn run:app\n```\n\nNOTE: You have to expose the host:port to where the LINE API server can access.\n\n\n## \ud83d\udd79\ufe0f Switching Types\n\nlinedify supports Agent and Chatbot for now. (You can add support for TextGenerator and Workflow on your own!)\n\nYou can switch the types by setting `dify_type` to the constructor of LineDify. Default is `DifyType.Agent`.\n\n```python\nline_dify = LineDify(\n line_channel_access_token=YOUR_CHANNEL_ACCESS_TOKEN,\n line_channel_secret=YOUR_CHANNEL_SECRET,\n dify_api_key=DIFY_API_KEY,\n dify_base_url=DIFY_BASE_URL,\n dify_user=DIFY_USER,\n dify_type=DifyType.Chatbot # <- DifyType.Agent or DifyType.Chatbot\n)\n```\n\n\n## \ud83d\udc8e Use UI Components\n\nImplement function to edit reply message below the decorator `@line_dify.to_reply_message`.\n\n```python\nfrom typing import List\nfrom linebot.v3.messaging import Message, TextMessage, QuickReply, QuickReplyItem, MessageAction\nfrom linedify.session import ConversationSession\n\n@line_dify.to_reply_message\nasync def to_reply_message(text: str, data: dict, session: ConversationSession) -> List[Message]:\n response_message = TextMessage(text=text)\n\n # Show QuickReply buttons when tool \"reservation\" was executed on Dify\n if tool := data.get(\"tool\"):\n if tool == \"reservation\":\n response_message.quick_reply = QuickReply([\n QuickReplyItem(action=MessageAction(label=\"Checkout\", text=\"Checkout\")),\n QuickReplyItem(action=MessageAction(label=\"Cancel\", text=\"Cancel\"))\n ])\n\n return [response_message]\n```\n\n## \ud83c\udfa8 Custom Logic\n\n### Event Validation\n\nUse `@line_dify.validate_event` to validate event before handling.\n\n```python\nbanned_users = [\"U123456\", \"U234567\"]\n\n@line_dify.validate_event\nasync def validate_event(event):\n line_user_id = event.source.user_id\n if line_user_id in banned_users:\n # Return the list of TextMessage to reply immediately without processing the event\n return [TextMessage(\"Forbidden\")]\n```\n\n\n### Handle events\n\nUse `@line_dify.event(event_type)` to customize event handlers.\n\n```python\n# Add handler for Postback event\n@line_dify.event(\"postback\")\nasync def handle_message_event(event: PostbackEvent):\n # Do something here\n # Return reply messages\n return [TextMessage(f\"Response for postback event: {event.postback.data}\")]\n\n# Add handler for unspecified event\n@line_dify.event()\nasync def handle_event(event):\n # Do something here\n # Return reply messages\n return [TextMessage(f\"Response for event type: {event.type}\")]\n```\n\n\n### Parse messages\n\nUse `@line_dify.parse_message(message_type)` to customize message parsers.\n\n```python\n@line_dify.parse_message(\"location\")\nasync def parse_location_message(message):\n text, _ = await line_dify.parse_location_message(message)\n map_image = get_map_image(message.address)\n return (text, map_image)\n```\n\n\n### Inputs\n\nUse `@line_dify.make_inputs` to customize `inputs` as arguments for Dify conversation threads.\n\n```python\n@line_dify.make_inputs\nasync def make_inputs(session: ConversationSession):\n # You can use session to customize inputs dynamically here\n inputs = {\n \"line_user_id\": session.user_id,\n \"favorite_food\": \"apple\"\n }\n \n return inputs\n```\n\n\n### Error Message\n\nUse `@line_dify.to_error_message` to customize reply message when error occurs.\n\n```python\n@line_dify.to_error_message\nasync def to_error_message(event: Event, ex: Exception, session: ConversationSession = None):\n # Custom logic here\n text = random.choice([\"Error \ud83e\udd72\", \"\ud83d\ude35 Something wrong...\", \"\ud83d\ude43\"])\n # Return reply messages\n return [TextMessage(text=text)]\n```\n\n\n## \ud83d\udcbe Conversation Session\n\nConversation sessions are managed by a database. By default, SQLite is used, but you can specify the file path or database type using `session_db_url`. For the syntax, please refer to SQLAlchemy's documentation.\n\nAdditionally, you can specify the session validity period with `session_timeout`. The default is 3600 seconds. If this period elapses since the last conversation, a new conversation thread will be created on Dify when the next conversation starts.\n\n```python\nline_dify = LineDify(\n line_channel_access_token=YOUR_CHANNEL_ACCESS_TOKEN,\n line_channel_secret=YOUR_CHANNEL_SECRET,\n dify_api_key=DIFY_API_KEY,\n dify_base_url=DIFY_BASE_URL,\n dify_user=DIFY_USER,\n session_db_url=\"sqlite:///your_sessions.db\", # SQLAlchemy database url\n session_timeout=1800, # Timeout in seconds\n)\n```\n\n\n## \ud83d\udc1d Debug\n\nSet `verbose=True` to see the request and response, both from/to LINE and from/to Dify.\n\n```python\nline_dify = LineDify(\n line_channel_access_token=YOUR_CHANNEL_ACCESS_TOKEN,\n line_channel_secret=YOUR_CHANNEL_SECRET,\n dify_api_key=DIFY_API_KEY,\n dify_base_url=DIFY_BASE_URL,\n dify_user=DIFY_USER,\n verbose=True\n)\n```\n\n\n## \u2696\ufe0f License\n\nlinedify is distributed under the Apache v2 license.\n\n(c)uezo, made with big \u2764\ufe0f in Tokyo.\n\n",
"bugtrack_url": null,
"license": "Apache v2",
"summary": "\ud83d\udcac\u26a1 linedify: Supercharging your LINE Bot with Dify power!",
"version": "0.3.1",
"project_urls": {
"Homepage": "https://github.com/uezo/linedify"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "94a66401051c52d2bb0299c0db12f4ae6e565909a9131ba886e1ae5f562f69da",
"md5": "486225d2888727fbd7b791a3c40880d8",
"sha256": "b3779ddab45deed957858a7aa0a61618cf4e2f461b8550f85f6cae9d6dc79396"
},
"downloads": -1,
"filename": "linedify-0.3.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "486225d2888727fbd7b791a3c40880d8",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 13325,
"upload_time": "2024-08-13T15:09:11",
"upload_time_iso_8601": "2024-08-13T15:09:11.855935Z",
"url": "https://files.pythonhosted.org/packages/94/a6/6401051c52d2bb0299c0db12f4ae6e565909a9131ba886e1ae5f562f69da/linedify-0.3.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-08-13 15:09:11",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "uezo",
"github_project": "linedify",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"requirements": [
{
"name": "aiohttp",
"specs": [
[
"==",
"3.9.5"
]
]
},
{
"name": "line-bot-sdk",
"specs": [
[
"==",
"3.11.0"
]
]
},
{
"name": "fastapi",
"specs": [
[
"==",
"0.111.0"
]
]
},
{
"name": "uvicorn",
"specs": [
[
"==",
"0.30.1"
]
]
},
{
"name": "SQLAlchemy",
"specs": [
[
"==",
"2.0.31"
]
]
}
],
"lcname": "linedify"
}