Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/workflows/dify-plugin-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Dify Plugin CI

on:
pull_request:
branches: [main]
workflow_dispatch:

permissions:
contents: read
pull-requests: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
dify-plugin-tests:
name: Dify Plugin Tests
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Run Dify plugin unit tests
run: python -m unittest discover -s dify-plugin-tdai-memory/tests

dify-plugin-manifest:
name: Dify Plugin Manifest
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Validate plugin manifest and package layout
run: python -m unittest discover -s dify-plugin-tdai-memory/tests -p test_plugin_manifest.py
14 changes: 14 additions & 0 deletions dify-plugin-tdai-memory/.difyignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
__pycache__/
*.pyc
.pytest_cache/
.venv/
tests/
scripts/
.git/
.DS_Store
.idea/
.vscode/
*.egg-info/
dist/
.env
.env.*
76 changes: 76 additions & 0 deletions dify-plugin-tdai-memory/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Dify Adapter Architecture

The Dify adapter is intentionally thin. It does not embed memory logic and does
not call an LLM directly. It maps Dify tool invocations onto the existing
TencentDB Agent Memory Gateway API.

## Call Chain

```text
Dify workflow
-> Dify tool plugin
-> TdaiGatewayClient
-> TencentDB Agent Memory Gateway
-> TdaiCore
-> L0/L1/L2/L3 memory pipeline and stores
```

## Responsibilities

| Layer | Responsibility |
| --- | --- |
| Dify workflow | Supplies `conversation_id`, user text, assistant text, and decides where recalled context is injected. |
| Dify tool plugin | Validates tool parameters, truncates prompt-bound output, and returns non-throwing JSON payloads. |
| `TdaiGatewayClient` | Sends JSON requests to Gateway endpoints and normalizes HTTP failures. |
| Gateway | Owns HTTP auth, request validation, session flush, recall, capture, and search routes. |
| `TdaiCore` | Owns host-neutral memory orchestration and the progressive memory pipeline. |

## Recall Flow

1. Dify calls `tdai_recall` before an LLM node.
2. The plugin sends `POST /recall` with `query` and `session_key`.
3. Gateway calls `TdaiCore.handleBeforeRecall`.
4. The plugin returns `{ "ok": true, "context": "..." }`.
5. The Dify workflow injects `context` into the LLM prompt.

## Read Paths

- `tdai_conversation_search` is the immediate `L0 read path`. It queries
`POST /search/conversations` and can read the raw captured turn right after
`tdai_capture` succeeds.
- `tdai_recall` is the structured recall path. It depends on the existing
Gateway/Core consolidation pipeline and may return an empty `context`
immediately after a single captured turn.

## Capture Flow

1. Dify calls `tdai_capture` after the assistant response is available.
2. The plugin sends `POST /capture` with the user message, assistant response,
and the same `session_key`.
3. Gateway calls `TdaiCore.handleTurnCommitted`.
4. L0 conversation capture happens synchronously; downstream extraction is
scheduled by the existing core pipeline.

## Session Model

Use Dify `conversation_id` as `session_key`. Do not use Dify run IDs because
they change between workflow executions and would fragment memory.

The current Gateway request types accept `user_id`, but core isolation still
follows the existing Gateway behavior. Treat `session_key` as the primary Dify
isolation key until Gateway/Core user scoping is extended.

## Credential Validation

`GET /health` is intentionally unauthenticated. To verify Bearer credentials,
the provider validation path sends one read-only memory search request:

```text
POST /search/memories
query = "__dify_credential_validation__"
limit = 1
```

This does not write memory, but it can appear in Gateway logs, traces, or
metrics. A future Gateway `OPTIONS` or authenticated handshake endpoint could
replace this probe without changing the tool API.
70 changes: 70 additions & 0 deletions dify-plugin-tdai-memory/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# TencentDB Agent Memory for Dify

This Dify tool plugin connects Dify workflows to TencentDB Agent Memory through
the local HTTP Gateway. It does not start the Gateway by itself.

## Quickstart

For a runnable smoke test that starts the Gateway, starts a mock Dify server,
and invokes the real `tdai_capture`, `tdai_conversation_search`, and
`tdai_recall` tool classes:

```bash
npm install
bash dify-plugin-tdai-memory/scripts/quickstart-gateway-mock-e2e.sh
```

For structured recall, the quickstart expects either:

- an existing configured Gateway via `TDAI_DIFY_GATEWAY_URL`, or
- a fresh Gateway that can run L1 extraction, for example with `TDAI_LLM_API_KEY`
set before the script starts it.

Manual setup:

1. Start the Gateway from the repository root:

```bash
npx tsx src/gateway/server.ts
```

2. In Dify, install this plugin directory and configure provider credentials:

- `gateway_url`: `http://127.0.0.1:8420`
- `gateway_api_key`: optional; must match `TDAI_GATEWAY_API_KEY` if Gateway auth is enabled
- `gateway_timeout_seconds`: optional, default `10`

3. Recommended workflow wiring:

- Before the LLM node: call `tdai_recall` with the current user message and Dify `conversation_id`.
- Inject the returned `context` into the system prompt or context field.
- After the assistant response: call `tdai_capture` with user/assistant content and the same `conversation_id`.
- At workflow `End`: call `tdai_session_end` to flush the current session buffer.

## Tools

| Tool | Gateway endpoint | Purpose |
| --- | --- | --- |
| `tdai_health` | `GET /health` | Check Gateway availability |
| `tdai_recall` | `POST /recall` | Recall memory context before generation |
| `tdai_capture` | `POST /capture` | Store a completed conversation turn |
| `tdai_memory_search` | `POST /search/memories` | Search structured L1 memories |
| `tdai_conversation_search` | `POST /search/conversations` | Search raw L0 conversations |
| `tdai_session_end` | `POST /session/end` | Flush the current session buffer |

## Notes

- Use Dify `conversation_id` as `session_key`; do not use transient run IDs.
- Use `tdai_conversation_search` as the immediate `L0 read path` for read-after-write verification.
- Treat `tdai_recall` as the structured recall path. The quickstart flushes via `tdai_session_end` and expects `memory_count >= 1` before it reports success.
- Tool calls return `{ "ok": false, "error": "..." }` on Gateway/network failure so workflows can continue without memory.
- Search outputs are truncated by default to 2000 characters to avoid oversized prompt injection. Set `max_chars` to `0` for unlimited output.
- The current Gateway accepts `user_id` in recall/capture request bodies, but core identity handling still uses the existing Gateway behavior. Treat `session_key` as the primary isolation key until Gateway/Core user scoping is extended.
- Provider credential validation sends a read-only `POST /search/memories` probe because `GET /health` is intentionally unauthenticated. The probe does not write memory, but it can appear in Gateway logs or metrics.

## Architecture Docs

- [Dify adapter architecture](ARCHITECTURE.md)
- Repository guide: `docs/dify-plugin-installation-guide.md`
- [Dify workflow diagram](../docs/dify-workflow-diagram.md)
- [Cross-platform adapter comparison](../docs/cross-platform-comparison.md)
5 changes: 5 additions & 0 deletions dify-plugin-tdai-memory/_assets/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions dify-plugin-tdai-memory/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import os

from dify_plugin import DifyPluginEnv, Plugin


DEFAULT_REQUEST_TIMEOUT = 120


def _max_request_timeout() -> int:
try:
timeout = int(os.environ.get("MAX_REQUEST_TIMEOUT", str(DEFAULT_REQUEST_TIMEOUT)))
except (TypeError, ValueError):
return DEFAULT_REQUEST_TIMEOUT
return timeout if timeout > 0 else DEFAULT_REQUEST_TIMEOUT


plugin = Plugin(DifyPluginEnv(MAX_REQUEST_TIMEOUT=_max_request_timeout()))


if __name__ == "__main__":
plugin.run()
34 changes: 34 additions & 0 deletions dify-plugin-tdai-memory/manifest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
version: 0.0.1
type: plugin
author: tencentdb-agent-memory
name: tdai_memory
label:
en_US: TencentDB Agent Memory
zh_Hans: 腾讯云数据库 Agent Memory
description:
en_US: Connect Dify workflows to TencentDB Agent Memory through the local HTTP Gateway.
zh_Hans: 通过本地 HTTP Gateway 将 Dify 工作流接入 TencentDB Agent Memory。
created_at: 2026-07-03T00:00:00.000Z
icon: icon.svg
tags:
- utilities
resource:
memory: 1048576
permission:
tool:
enabled: true
model:
enabled: false
llm: false
plugins:
tools:
- provider/tdai_memory.yaml
meta:
version: 0.0.1
arch:
- amd64
- arm64
runner:
language: python
version: "3.12"
entrypoint: main
30 changes: 30 additions & 0 deletions dify-plugin-tdai-memory/provider/tdai_memory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""Dify provider for TencentDB Agent Memory Gateway credentials."""

from __future__ import annotations

from typing import Any

from dify_plugin import ToolProvider
from dify_plugin.errors.tool import ToolProviderCredentialValidationError

from tools.client import TdaiGatewayClient, TdaiGatewayError


class TdaiMemoryProvider(ToolProvider):
"""Validate Dify provider credentials against the Gateway."""

def _validate_credentials(self, credentials: dict[str, Any]) -> None:
gateway_url = str(credentials.get("gateway_url") or "").strip()
if not gateway_url:
raise ToolProviderCredentialValidationError("Gateway URL is required")

try:
client = TdaiGatewayClient.from_credentials(credentials)
# `/health` is intentionally unauthenticated, so use a read-only
# search request to validate Bearer credentials when auth is on.
client.search_memories("__dify_credential_validation__", limit=1)
except TdaiGatewayError as exc:
message = "Gateway credential validation failed"
if exc.status_code:
message = f"{message} (HTTP {exc.status_code})"
raise ToolProviderCredentialValidationError(message) from exc
56 changes: 56 additions & 0 deletions dify-plugin-tdai-memory/provider/tdai_memory.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
identity:
author: tencentdb-agent-memory
name: tdai_memory
label:
en_US: TencentDB Agent Memory
zh_Hans: 腾讯云数据库 Agent Memory
description:
en_US: Local long-term memory tools backed by TencentDB Agent Memory Gateway.
zh_Hans: 基于 TencentDB Agent Memory Gateway 的本地长期记忆工具。
icon: icon.svg
tags:
- utilities
credentials_for_provider:
gateway_url:
type: text-input
required: true
label:
en_US: Gateway URL
zh_Hans: Gateway 地址
placeholder:
en_US: http://127.0.0.1:8420
zh_Hans: http://127.0.0.1:8420
help:
en_US: Start the TDAI Gateway before using these tools.
zh_Hans: 使用这些工具前请先启动 TDAI Gateway。
gateway_api_key:
type: secret-input
required: false
label:
en_US: Gateway API Key
zh_Hans: Gateway API Key
placeholder:
en_US: Optional Bearer token
zh_Hans: 可选 Bearer Token
help:
en_US: Must match TDAI_GATEWAY_API_KEY when Gateway auth is enabled.
zh_Hans: Gateway 开启鉴权时必须与 TDAI_GATEWAY_API_KEY 一致。
gateway_timeout_seconds:
type: text-input
required: false
label:
en_US: Timeout Seconds
zh_Hans: 超时秒数
placeholder:
en_US: "10"
zh_Hans: "10"
extra:
python:
source: provider/tdai_memory.py
tools:
- tools/tdai_health.yaml
- tools/tdai_recall.yaml
- tools/tdai_capture.yaml
- tools/tdai_memory_search.yaml
- tools/tdai_conversation_search.yaml
- tools/tdai_session_end.yaml
17 changes: 17 additions & 0 deletions dify-plugin-tdai-memory/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[project]
name = "dify-plugin-tdai-memory"
version = "0.0.1"
description = "Connect Dify workflows to TencentDB Agent Memory through the local HTTP Gateway."
readme = "README.md"
requires-python = ">=3.12"
authors = [
{ name = "tencentdb-agent-memory" },
]
license = { text = "MIT" }
dependencies = [
"dify_plugin~=0.9.0",
]

[build-system]
requires = ["setuptools>=64.0", "wheel"]
build-backend = "setuptools.build_meta"
Loading
Loading