LINE AG-UI Adapter for Python
A lightweight adapter that bridges LINE Messaging API and AG-UI using line-bot-sdk-python v3.
It converts LINE webhook message events into AG-UI requests, sends them to an AG-UI server, and converts the final AG-UI response back into LINE reply messages.
日本語での紹介記事はこちら Description in Japanese
Features
- Convert LINE inbound messages to AG-UI input
- text
- image / audio / video as multimodal input parts
- file as multimodal input parts only for supported document/text/spreadsheet extensions
- Convert AG-UI responses back to LINE reply messages
- text
- image / audio / video when
source.type == "url" - document responses as a text message containing the document URL
- Add middleware hooks before and after the AG-UI request
- Buffer the final AG-UI response before replying, which fits LINE's non-streaming reply flow
Installation
Using uv:
uv add line-agui-adapter
Using pip:
pip install line-agui-adapter
If you want to run the FastAPI example as well:
uv add fastapi uvicorn python-dotenv
Getting Started
The FastAPI example is available in examples/fastapi/main.py.
The example assumes that:
- a LINE channel is already configured
- the webhook server is exposed over HTTPS and reachable by LINE
- an AG-UI server is already running
- the required environment variables are set
If you do not have an AG-UI server yet, you can use the sample implementation under tests/servers. For example, tests/servers/google_adk/main.py can be used as a simple AG-UI-compatible test server.
Example startup:
cp .env.example .env
uv run uvicorn examples.fastapi.main:app --port 8000
FastAPI Example
import os
from typing import cast
from dotenv import load_dotenv
from fastapi import FastAPI, Header, HTTPException, Request
from linebot.v3 import WebhookParser, WebhookPayload
from linebot.v3.exceptions import InvalidSignatureError
from linebot.v3.messaging import (
ApiClient,
Configuration,
MessagingApi,
MessagingApiBlob,
ReplyMessageRequest,
ShowLoadingAnimationRequest,
)
from linebot.v3.webhooks import MessageEvent
from line_agui_adapter import AguiHttpClient, LineAguiAdapter, create_content_fetcher
load_dotenv()
app = FastAPI()
parser = WebhookParser(channel_secret=os.environ["LINE_CHANNEL_SECRET"])
configuration = Configuration(access_token=os.environ["LINE_CHANNEL_ACCESS_TOKEN"])
agui_client = AguiHttpClient(
endpoint=os.environ["AGUI_ENDPOINT"],
headers=(
{"Authorization": f"Bearer {os.environ['AGUI_AUTH_TOKEN']}"}
if os.environ.get("AGUI_AUTH_TOKEN")
else {}
),
)
async def before_agui(request):
request.forwarded_props["tenant_id"] = "example-tenant"
request.forwarded_props["source"] = "line-fastapi-example"
return request
def after_agui(response):
for message in response.assistant_messages:
if isinstance(message.content, str) and message.content:
message.content = f"[AG-UI] {message.content}"
return response
@app.post("/callback")
async def callback(
request: Request, x_line_signature: str = Header(...)
) -> dict[str, bool]:
body = (await request.body()).decode("utf-8")
try:
payload = cast(
WebhookPayload, parser.parse(body, x_line_signature, as_payload=True)
)
except InvalidSignatureError as exc:
raise HTTPException(status_code=400, detail="invalid signature") from exc
with ApiClient(configuration) as api_client:
line_api = MessagingApi(api_client)
blob_api = MessagingApiBlob(api_client)
adapter = LineAguiAdapter(
agui_client=agui_client,
content_fetcher=create_content_fetcher(blob_api),
)
adapter.pipeline.add_before(before_agui)
adapter.pipeline.add_after(after_agui)
for event in payload.events or []:
if not isinstance(event, MessageEvent):
continue
if event.mode == "standby" or not event.reply_token:
continue
user_id = getattr(event.source, "user_id", None)
if user_id:
line_api.show_loading_animation(
ShowLoadingAnimationRequest(
chatId=user_id,
loadingSeconds=60,
)
)
messages = await adapter.handle_event(event)
line_api.reply_message(
ReplyMessageRequest(
replyToken=event.reply_token,
messages=messages,
notificationDisabled=False,
)
)
return {"ok": True}
Middleware Hooks
Middleware hooks let you modify the AG-UI request before sending it, or modify the AG-UI response before converting it back to LINE messages.
async def before_hook(request):
request.forwarded_props["tenant_id"] = "tenant-a"
return request
def after_hook(response):
for message in response.assistant_messages:
if isinstance(message.content, str) and message.content:
message.content = f"[AG-UI] {message.content}"
return response
adapter.pipeline.add_before(before_hook)
adapter.pipeline.add_after(after_hook)
Example Environment Variables
Typical variables used by the FastAPI example:
LINE_CHANNEL_SECRETLINE_CHANNEL_ACCESS_TOKENAGUI_ENDPOINTAGUI_AUTH_TOKEN(optional)LINE_AGUI_FASTAPI_EXAMPLE_LOG_LEVEL(optional)
Set LINE_AGUI_FASTAPI_EXAMPLE_LOG_LEVEL to a standard Python logging level such as DEBUG, INFO, WARNING, ERROR, or CRITICAL if you want explicit logging configuration in the example app.
Notes
- LINE replies are generated from the final AG-UI response rather than from streaming output.
- Binary output is only converted to LINE media messages when the AG-UI response provides a URL-based source.
- LINE
filemessages are only forwarded as binary input when their extension is supported by the OpenAI file inputs guide, such as.pdf,.md,.txt,.json,.html,.xml,.docx,.pptx,.csv, and.xlsx. Unsupported extensions are reduced to the fallback text hint only. - The included Google ADK test server in tests/servers/google_adk/main.py disables input blob artifact replacement so image inputs can be passed to the model as actual inline data.
Metadata
Release files for line-agui-adapter 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| line_agui_adapter-0.1.2.tar.gz | 12.8 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| line_agui_adapter-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 28.4 kB
Release files / line_agui_adapter-0.1.2.tar.gz
| Download URL | line_agui_adapter-0.1.2.tar.gz |
|---|---|
| Size | 12.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b2cf568e92dc74c4631d93aedddaba98348b06cd695a4a643bb917c5e6105749
|
|
BLAKE2b-256 checksum How to use checksums |
5cc0bbd958d7abba1fd320a144154d813aba933d140e6b24a62c92855bf4b57f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.4 {"installer":{"name":"uv","version":"0.11.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / line_agui_adapter-0.1.2-py3-none-any.whl
| Download URL | line_agui_adapter-0.1.2-py3-none-any.whl |
|---|---|
| Size | 15.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b9babd7ebb68678bbbeb4a945f3d6ede88cfd3a5d1034aa541dc718da57fd7c6
|
|
BLAKE2b-256 checksum How to use checksums |
c30e9c288274c777d3fdf82a0ab1d19d471282e9ea132e3f806b5b187dabd495
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.4 {"installer":{"name":"uv","version":"0.11.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for line-agui-adapter 0.1.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.