deer-flow/docs/guide/api-and-client.md
Xinmin Zeng 2a97ec3e58
docs: turn README into an index and move reference detail to docs/guide (#8113)
* docs: turn README into an index and move reference detail to docs/guide

The README had grown to 393 KB because every change appended its notes there. The text moves verbatim into 18 topic guides under docs/guide/; only headings are re-leveled and relative links re-pointed. The README keeps the introduction, a quick start and a guide index. Contributor test notes move to CONTRIBUTING.md.

* docs: send user docs to topic guides and budget the root README

The rule 'update README.md for user-facing changes' is what grew the README. Guidance now points to docs/guide/, and check_agent_guidance.py fails when the root README exceeds 16 KiB (AG003).

* chore: remove unreferenced screenshots and finished plan notes

pr-build/ holds two PR screenshots (3 MB) from #1986. plans/ holds the plan for the merged #4049. docs/SKILL_NAME_CONFLICT_FIX.md is a February change note. Nothing references them.

* docs: route always-loaded AGENTS.md files to scoped guides

* docs(agents): cut scoped guides to scope, verify, traps, decisions

Each AGENTS.md keeps four kinds of content: the scope of the directory, the
verification commands, traps a local test run does not show, and decisions the
code cannot state. Values, field lists, function inventories, test names, issue
numbers and history are deleted, because the code, the tests and git state them.

Search and fetch providers share one community guide. Four guides with no
remaining content are removed. The size check now allows 6 KiB per file (hard
limit 8 KiB) and 24 KiB per chain (hard limit 32 KiB).

* style: format frontend guide table and guidance test imports

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-11-11 13:26:17 +08:00

8.3 KiB
Raw Blame History

API Access and Python Client

Existing-Run Stream Actions

Existing-run SSE joins are observation-only on GET: supplying action=interrupt|rollback returns 405. Cancellation on this stream route is POST-only and requires the runs:cancel permission. Accordingly, the OpenAPI contract exposes action and wait only on POST; the GET operation exposes only its path parameters.

Personal Access Tokens

Non-interactive clients (CI pipelines, scripts, server-to-server integrations) can call the Gateway API with a personal access token (PAT) instead of a browser session. Create one while logged in via POST /api/v1/auth/pats — the raw dfp_... value is shown exactly once; only its SHA-256 digest is stored — then send it as a Bearer credential:

POST /api/threads/search
Authorization: Bearer dfp_...
Content-Type: application/json

{}

Each token runs with its owning user's identity (owner filtering and per-user memory keep working), carries a scope set that can only narrow that user's permissions, and is admitted only to the thread/run lifecycle routes — every other route answers 403 to PAT callers, and a PAT never carries admin capability. Tokens can be listed and revoked at any time; revocation is immediate. PATs require a database backend (SQLite/PostgreSQL). Full reference: API Reference — Personal Access Tokens.

Embedded Python Client

User messages are framed as untrusted input. Reserved XML-like tags are escaped for model context only when present; tag-free URLs and shell commands retain their ampersands unchanged. Encoded payloads include a note to decode exactly once when reproducing the original text in files, code, or tool-call arguments. This preserves XML angle brackets and literal HTML entities in verbatim-content requests while keeping user-supplied tags separate from framework instructions. Copied encoding notes are treated as user text; only request-local program state permits reuse of an already encoded frame.

chat() and stream() preserve text appended by model middleware before the first state snapshot, emitting only the suffix after the streamed text.

For DeerFlowClient(agent_name="researcher"), the named agent's mcp_plugins selection applies to both the lead agent and its task / batch_task delegations: null inherits all enabled MCP plugins, [] selects none, and installation IDs select only those plugins. Call client.reset_agent() after editing the saved agent configuration to refresh the selection.

DeerFlowClient.stream() includes summary_text in each values event. This is the current compacted context summary, or None when absent. Consumers can record changes without reading checkpoint internals; repeated snapshots may carry the same summary, and an initial snapshot may already contain one from an earlier turn.

DeerFlow can be used as an embedded Python library without running the full HTTP services. The DeerFlowClient provides direct in-process access to all agent and Gateway capabilities, returning the same response schemas as the HTTP Gateway API.

The HTTP Gateway exposes DELETE /api/threads/{thread_id} to remove a thread's files, checkpoints, and metadata. If checkpoint deletion fails, the Gateway returns 500 and retains the owner record for retry. Thread cleanup API

An embedded DeerFlowClient(config_path="custom/config.yaml") keeps the process config singleton on that file, even after a working-directory change. Later edits to the file are still detected. Call reload_app_config() without a path to resume ambient config discovery, or reset_app_config() to clear the cached selection.

For database-backed run events, deleting a run preserves its thread's sequence watermark. Thread deletion removes that watermark once no events remain, allowing a recreated thread to restart at sequence 1. Owner-scoped deletion preserves the watermark when another owner's events remain.

Single-process JSONL event storage also retains the thread sequence watermark across run deletion and restarts, so clients using after_seq do not miss later messages. Keep runs/.seq-watermark with the run files when backing up this backend. New or replaced watermarks inherit the deleted run file's permission bits. Deleting the complete thread removes the watermark and resets allocation.

Thread IDs may be supplied by callers and do not have to be UUIDs. Explicit IDs must contain 1–64 ASCII letters, digits, hyphens, or underscores (^[A-Za-z0-9_-]{1,64}$). DeerFlow generates a UUID only when thread_id is omitted or None; an explicitly supplied empty string is invalid. Concurrent POST /api/threads requests and automatic run metadata creation share same-ID admission in one Gateway, preserving the first thread's metadata and status. HTTP retries also preserve its current checkpoint. SQL-backed Gateways retain duplicate-insert recovery across workers. Existing route-addressable threads created under older, looser rules remain readable and deletable, but cannot start new runs or create new filesystem or sandbox state. Legacy deletion skips local path cleanup when the ID is not safe under the canonical contract. For canonical legacy threads whose conversation exists only in LangGraph checkpoints, DeerFlow seeds an empty run-event feed from the checkpoint before the first new run so /messages/page keeps both the old and new turns.

from deerflow.client import DeerFlowClient

client = DeerFlowClient()

# Chat
response = client.chat("Analyze this paper for me", thread_id="my-thread")

# Streaming (LangGraph SSE protocol: values, messages-tuple, end)
for event in client.stream("hello"):
    if event.type == "messages-tuple" and event.data.get("type") == "ai":
        print(event.data["content"])
    elif event.type == "messages-tuple" and event.data.get("type") == "tool" and "artifact" in event.data:
        # Structured tool artifacts (for example, ask_clarification cards)
        # are preserved when the ToolMessage provides one.
        print(event.data["artifact"])

# Configuration & management — returns Gateway-aligned dicts
models = client.list_models()        # {"models": [...]}
skills = client.list_skills()        # {"skills": [...]}
client.update_skill("web-search", enabled=True)
client.upload_files("thread-1", ["./report.pdf"])  # {"success": True, "files": [...]}
client.set_goal("thread-1", "finish the implementation and make all tests pass")
client.get_goal("thread-1")       # {"goal": {...}} or {"goal": None}
client.clear_goal("thread-1")

When continuing a thread, values still contains the full conversation state, while messages-tuple and end.usage cover only the current turn.

The embedded client's end.usage sums usage deltas across streamed chunks, including providers such as native Gemini that report usage more than once per reply. The completed message's usage is reconciled without counting those chunks again; a lower completed-message count does not subtract tokens already reported by chunks. Signed provider corrections in chunks remain supported, as do providers that report usage only at completion.

Concurrent upload_files() calls preserve filenames absent at the start of each call by assigning numeric suffixes and returning the actual saved names. The same rule applies to new Markdown companions. Exact names already present retain replacement behavior. Convertible files use one private source capture through all filename retries and conversion, preserving the original permissions and timestamps. Detected staging-file substitution is rejected; staging cleanup failures are logged without masking the upload outcome.

The HTTP Gateway accepts values, messages-tuple, updates, debug, tasks, checkpoints, and custom stream modes. Unsupported modes such as messages and events, unsupported non-default run options such as webhooks, delayed execution, or multitask_strategy="enqueue", and undeclared SDK options such as checkpoint durability overrides return 422 before execution instead of being silently ignored or downgraded.

All dict-returning methods are validated against Gateway Pydantic response models in CI (TestGatewayConformance), ensuring the embedded client stays in sync with the HTTP API schemas. See backend/packages/harness/deerflow/client.py for full API documentation.