Conversation
There was a problem hiding this comment.
🟢 Approval recommended
Only minor documentation spelling nits were found, and the integration codepaths are backed by unit and e2e tests.
Pull request overview
Adds an experimental Dify integration to PowerContext, providing (1) a Dify tool plugin exposing typed HTTP-backed memory operations and (2) a separate legacy Workflow Agent strategy that automatically recalls/captures bounded execution evidence, alongside supporting docs, test coverage, and local validation tooling.
Changes:
- Introduce two independently installable Dify plugin packages:
powercontext(tools) andpowercontext_agent(Function Calling strategy with automatic memory hooks). - Add bridge/runtime code for scope-binding based routing (no default-scope fallback), event capture with redaction/bounds, and conservative context window compaction.
- Add integration manifests, docs (EN/ZH), packaging helpers, and both unit + e2e coverage plus a
make dify-testtarget.
File summaries
| File | Description |
|---|---|
| tests/e2e/test_dify_chain.py | E2E test exercising capture → extraction → recall against real PC ASGI + SQLite. |
| scripts/integration_manifest.py | Adds a dify_tools probe to surface tool IDs from the Dify provider manifest. |
| pyproject.toml | Excludes integrations/dify from the root project’s exclude list (keeping it out of default tooling scope). |
| Makefile | Adds dify-test and dify-sources helper targets for integration validation and source packaging. |
| integrations/dify/tests/test_strategy.py | Tests strategy registration and Function Calling behavior (including compaction behavior). |
| integrations/dify/tests/test_bridge.py | Tests binding key isolation, missing-binding behavior, capture bounding/redaction, and retry-stable source IDs. |
| integrations/dify/tests/conftest.py | Adjusts import paths so both plugin packages can be tested without installation. |
| integrations/dify/README.md | Top-level integration documentation, protocol overview, and development/deployment guidance. |
| integrations/dify/pytest.ini | Test configuration for the integration subproject. |
| integrations/dify/pyproject.toml | Defines the pinned Dify SDK dev environment and lint/typecheck configuration for the integration. |
| integrations/dify/provision_scope.py | CLI helper to provision a Dify identity binding using a PC admin credential. |
| integrations/dify/powercontext/tools/search_memory.yaml | Dify tool schema for search_memory. |
| integrations/dify/powercontext/tools/search_memory.py | Tool shim wiring search_memory to the shared operation implementation. |
| integrations/dify/powercontext/tools/revise_memory_entry.yaml | Dify tool schema for revise_memory_entry. |
| integrations/dify/powercontext/tools/revise_memory_entry.py | Tool shim wiring revise_memory_entry to the shared operation implementation. |
| integrations/dify/powercontext/tools/retire_memory_entry.yaml | Dify tool schema for retire_memory_entry. |
| integrations/dify/powercontext/tools/retire_memory_entry.py | Tool shim wiring retire_memory_entry to the shared operation implementation. |
| integrations/dify/powercontext/tools/remember_memory.yaml | Dify tool schema for remember_memory. |
| integrations/dify/powercontext/tools/remember_memory.py | Tool shim wiring remember_memory to the shared operation implementation. |
| integrations/dify/powercontext/tools/prepare_context.yaml | Dify tool schema for prepare_context. |
| integrations/dify/powercontext/tools/prepare_context.py | Tool shim wiring prepare_context to the shared operation implementation. |
| integrations/dify/powercontext/tools/get_memory_entry.yaml | Dify tool schema for get_memory_entry. |
| integrations/dify/powercontext/tools/get_memory_entry.py | Tool shim wiring get_memory_entry to the shared operation implementation. |
| integrations/dify/powercontext/tools/flush_memory.yaml | Dify tool schema for flush_memory. |
| integrations/dify/powercontext/tools/flush_memory.py | Tool shim wiring flush_memory to the shared operation implementation. |
| integrations/dify/powercontext/tools/capture_event.yaml | Dify tool schema for capture_event. |
| integrations/dify/powercontext/tools/capture_event.py | Tool shim wiring capture_event to the shared operation implementation. |
| integrations/dify/powercontext/requirements.txt | Pins Dify SDK and PowerContext client dependency (source commit) for the tool plugin package. |
| integrations/dify/powercontext/README.md | Tool plugin package README (scope/binding model, tool list, lifecycle notes). |
| integrations/dify/powercontext/provider/powercontext.yaml | Dify provider manifest declaring credentials and tool surfaces. |
| integrations/dify/powercontext/provider/powercontext.py | Provider credential validation via read-only capabilities endpoint. |
| integrations/dify/powercontext/PRIVACY.md | Tool plugin privacy policy. |
| integrations/dify/powercontext/operation.py | Shared Dify Tool adapter implementing execution + sanitized error outcomes. |
| integrations/dify/powercontext/manifest.yaml | Dify plugin manifest metadata for the tool package. |
| integrations/dify/powercontext/main.py | Tool plugin entrypoint. |
| integrations/dify/powercontext/LICENSE | Apache-2.0 license text shipped with the tool package. |
| integrations/dify/powercontext/bridge.py | Typed bridge enforcing host-controlled identity/binding resolution and operation routing. |
| integrations/dify/powercontext/.difyignore | Dify packaging ignore file for the tool package. |
| integrations/dify/powercontext/_assets/icon.svg | Tool package icon asset. |
| integrations/dify/powercontext_agent/strategies/window.py | Conservative byte-based budgeting and compaction logic preserving tool-call/result pairing. |
| integrations/dify/powercontext_agent/strategies/memory.py | Callback wrapper ensuring identity is host-controlled and memory tools degrade cleanly. |
| integrations/dify/powercontext_agent/strategies/function_calling.yaml | Strategy declaration for legacy Workflow Agent selection/configuration. |
| integrations/dify/powercontext_agent/strategies/function_calling.py | Function Calling strategy with deterministic memory hooks, bounded input, and capture events. |
| integrations/dify/powercontext_agent/requirements.txt | Pins Dify SDK for the agent strategy package. |
| integrations/dify/powercontext_agent/README.md | Agent strategy package README (setup and behavior). |
| integrations/dify/powercontext_agent/provider/powercontext_agent.yaml | Strategy provider manifest for Dify. |
| integrations/dify/powercontext_agent/PRIVACY.md | Agent strategy privacy policy. |
| integrations/dify/powercontext_agent/manifest.yaml | Dify plugin manifest metadata for the agent strategy package. |
| integrations/dify/powercontext_agent/main.py | Agent strategy plugin entrypoint. |
| integrations/dify/powercontext_agent/LICENSE | Apache-2.0 license text shipped with the agent strategy package. |
| integrations/dify/powercontext_agent/.difyignore | Dify packaging ignore file for the agent strategy package. |
| integrations/dify/powercontext_agent/_assets/icon.svg | Agent strategy package icon asset. |
| integrations/dify/package_sources.py | Creates reviewable source archives for both packages (non-CLI .difypkg substitute). |
| integrations/dify/local/VALIDATION.md | Local deployed validation report and limitations (ZH). |
| integrations/dify/local/run.py | Foreground runner for isolated local services and plugin processes. |
| integrations/dify/local/register_provider.py | Helper to store PC credentials in the local Dify workspace via the real credential owner. |
| integrations/dify/local/README.md | Local CE + daemon + PC integration runbook (ZH). |
| integrations/dify/local/prepare.py | Generates isolated local env/config without copying user credentials. |
| integrations/dify/local/live_workflow.py | Live workflow script validating write/recall/isolation and capture redaction via real services. |
| integrations/dify/local/live_users.py | Live Service API script validating per-user isolation with actual EndUser.id bindings. |
| integrations/dify/local/live_native_config.py | Live script validating Agent V2 external-memory config persistence without leaking secrets. |
| integrations/dify/local/init.sql | Local DB bootstrap for plugin daemon DB + pgvector extension. |
| integrations/dify/local/console.py | Authenticated Console API helper for the isolated acceptance workspace. |
| integrations/dify/local/compose.yaml | Docker Compose stack for isolated DB/Redis/daemon/sandbox services. |
| integrations/dify/generate_tool_schemas.py | Generates nested Dify request schemas from typed PC HTTP models (scope_id removed). |
| integrations/dify/ACCEPTANCE.md | Deployed acceptance checklist for official packaging + real-model validation. |
| integrations/capabilities.toml | Registers Dify integration/toolset capabilities and evidence pointers. |
| docs/zh/docs/integrations/meta.json | Adds dify to the Chinese integrations nav. |
| docs/zh/docs/integrations/dify.md | Chinese integration documentation page. |
| docs/zh/docs/integrations/capabilities.md | Adds Dify to the capabilities table (ZH). |
| docs/en/docs/integrations/meta.json | Adds dify to the English integrations nav. |
| docs/en/docs/integrations/dify.md | English integration documentation page. |
| docs/en/docs/integrations/capabilities.md | Adds Dify to the capabilities table (EN). |
Review details
- Files reviewed: 71/74 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| Dify stores provider credentials using its credential mechanism. PC tool transport uses HTTPS by default outside loopback; | ||
| un-encrypted private-network HTTP requires explicit administrator opt-in. Captured Sources and derived Memory are retained |
| Dify stores provider credentials using its credential mechanism. PC tool transport uses HTTPS by default outside loopback; | ||
| un-encrypted private-network HTTP requires explicit administrator opt-in. Captured Sources and derived Memory are retained |
Teingi
left a comment
There was a problem hiding this comment.
Three reproducible issues below. The existing 11 plugin tests, HTTP/SQLite test, Ruff and type checks passed. Additional probes used the pinned SDK, a loopback HTTP server, and the real PC HTTP app backed by SQLite. Model responses in the strategy and compaction probes were simulated; full CE/plugin-daemon/model acceptance was not run.
| ) | ||
| try: | ||
| result_messages = self._invoke_tool(state.tools, call) | ||
| result = "\n".join(_tool_text(item) for item in result_messages) |
There was a problem hiding this comment.
[P1] Preserve structured tool results until capture redaction
Joining the messages here bypasses credential-field redaction when a tool returns text followed by a JSON message containing api_key (or multiple JSON messages). The resulting string is no longer a valid JSON document, so the bridge's sanitizer treats it as free text and preserves the secret. I reproduced this through the strategy and the real PC HTTP/SQLite app: capture returned accepted, and reading the Source back exposed the synthetic key. The single-JSON-message control was correctly redacted. Please pass structured messages to capture, or redact each message before joining, and cover mixed text/JSON and multiple-JSON results.
| identity_value = {"app_id": self.session.app_id, "subject_id": self.runtime.user_id} | ||
| identity = MemoryIdentity.model_validate(_object(identity_value)) | ||
| request = _object(tool_parameters.get("request", {})) | ||
| result = asyncio.run(self._execute(connection, identity, request)) |
There was a problem hiding this comment.
[P2] Avoid asyncio.run in concurrent gevent workers
The pinned Dify SDK applies gevent monkey-patching, so its executor workers can share the native thread's running asyncio loop. With that SDK and a loopback HTTP server, two sequential tool invocations both succeeded, but two concurrent invocations caused one to raise RuntimeError: asyncio.run() cannot be called from a running event loop. It returned error/invalid_response without sending an HTTP request. This affects all operations using this adapter, so overlapping workflows can lose recall, writes or capture; automatic callbacks also suppress subsequent attempts for that run. Please use a gevent-compatible execution boundary and add a concurrent-invocation regression test.
| ) | ||
| strategy.increase_usage(usage, result.usage) | ||
| summaries.append(result.message.get_text_content()) | ||
| return "\n".join(summaries) |
There was a problem hiding this comment.
[P2] Bound the combined summary, not just each chunk
Each chunk gets its own output allowance, but all summaries are concatenated without a total budget or a reduction pass. With 25 historical exchanges (~42 KB) and a 12,000-byte input budget, simulated summaries of only 990 ASCII characters each caused 19 model calls and over 18 KB of summary text. fit_window then raised Compacted history still exceeds the model input budget, aborting the agent even though the current task and retained tail fit. Please allocate a total summary budget from the remaining context space, or recursively reduce the summaries, and test multiple realistic-length chunk summaries.
Which issue or RFC does this PR close?
NONE.
However, we first need to wait for Dify's PR langgenius/dify#42152 to be merged.
Rationale for this change
Dify applications need a way to recall PowerContext memory and retain useful execution evidence across runs. This change adds two independently installable plugins: memory tools for workflows and an Agent strategy that automatically recalls context and captures visible execution events.
What changes are included in this PR?
Are there any user-facing changes?
Users can configure a PowerContext tool credential and invoke memory tools in workflows. Legacy Workflow Agents can select the PowerContext Function Calling strategy for automatic memory callbacks.
Native Agent V2 automatic memory requires the companion Dify runtime/API/Web changes. Installing these plugins alone does not add that capability to an unmodified Dify release. Existing PowerContext HTTP contracts are unchanged.
The connection needs a reachable PC Server and provisioned Scope bindings. Binding identity selects data; deployments must enforce authorization separately. The plugin currently pins its PC client to an immutable source commit.
How was this change tested?
make dify-test: Ruff and ty passed; 11 plugin tests and 1 PC HTTP/SQLite integration test passed. Strategy tests use simulated model responses, and the extraction test uses a deterministic pipeline.live_workflow.py: verified direct memory writes, recall, business Scope isolation, missing-binding behavior, and persisted Source redaction through actual workflow runs.live_users.py: verified cross-request recall for Alice and empty recall for Bob through the local published Workflow Service API.live_native_config.py: verified V2 configuration save/reload and credential references without exposing the PC token, using the companion Dify changes.Real-model automatic Agent recall/capture, model-based extraction, long-context compaction, and pause/resume acceptance remain outstanding. Official
.difypkginstallation and Marketplace publication have not been performed. Seeintegrations/dify/local/VALIDATION.mdfor the deployment evidence and its limits.AI usage statement