linedify


Namelinedify JSON
Version 0.3.1 PyPI version JSON
download
home_pagehttps://github.com/uezo/linedify
SummaryđŸ’Ŧ⚡ linedify: Supercharging your LINE Bot with Dify power!
upload_time2024-08-13 15:09:11
maintaineruezo
docs_urlNone
authoruezo
requires_pythonNone
licenseApache v2
keywords
VCS
bugtrack_url
requirements aiohttp line-bot-sdk fastapi uvicorn SQLAlchemy
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # 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"
}
        
Elapsed time: 0.28809s