mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-14 10:17:55 +00:00
Docs revamp
This commit is contained in:
1 parent
1acb836332
commit
5c19d972d2
27 files changed
+1101
-39
No files matched your search
@@ -15,6 +15,10 @@ export default {
|
||||
"title": "🪝 Agent Webhooks",
|
||||
"href": "/Agents/webhooks"
|
||||
},
|
||||
"notifications": {
|
||||
"title": "📡 Realtime Events",
|
||||
"href": "/Agents/notifications"
|
||||
},
|
||||
"nodes": {
|
||||
"title": "🧩 Workflow Nodes",
|
||||
"href": "/Agents/nodes"
|
||||
|
||||
@@ -345,3 +345,51 @@ curl -X POST http://localhost:7091/stream \
|
||||
- If the selected model/provider does not support a file type natively, DocsGPT falls back to parsed text content.
|
||||
- For providers that support images but not native PDF file attachments, DocsGPT can convert PDF pages to images (synthetic PDF support).
|
||||
- Attachments are user-scoped. Upload and query must be done under the same user context (same API key owner or same JWT user).
|
||||
|
||||
## Agent Portability (Export & Import)
|
||||
|
||||
Agents can be exported to a portable YAML file and imported into another DocsGPT instance (or back into the same one). This is how you move an agent between environments or share a reproducible definition.
|
||||
|
||||
| Method | Path | Description |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/api/export_agent?id=<agent_id>` | Download the agent as a `*.agent.yaml` file. |
|
||||
| `POST` | `/api/import_agent/plan` | Dry-run an import: parse a YAML file and return a resolution plan without creating anything. |
|
||||
| `POST` | `/api/import_agent` | Import a YAML file, creating the agent as a **draft**. |
|
||||
|
||||
```bash
|
||||
# Export
|
||||
curl -L "https://your-docsgpt/api/export_agent?id=<agent_id>" \
|
||||
-H "Authorization: Bearer <token>" -o my-agent.agent.yaml
|
||||
|
||||
# Preview what an import would do
|
||||
curl -X POST https://your-docsgpt/api/import_agent/plan \
|
||||
-H "Authorization: Bearer <token>" \
|
||||
--data-binary @my-agent.agent.yaml
|
||||
|
||||
# Import (creates a draft agent)
|
||||
curl -X POST https://your-docsgpt/api/import_agent \
|
||||
-H "Authorization: Bearer <token>" \
|
||||
--data-binary @my-agent.agent.yaml
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- **Secrets are stripped on export** — API keys and tool credentials are never written into the YAML, so you re-enter them after import.
|
||||
- The **plan** endpoint resolves references (sources, tools, prompts) and reports what will be created or matched, so you can review before committing.
|
||||
- Imported tool URLs are validated against SSRF protections, the same as when creating tools normally.
|
||||
- **Workflow agents cannot be exported yet** — exporting one returns `400`.
|
||||
|
||||
## Searching Conversations
|
||||
|
||||
Search across your conversations by name and message content:
|
||||
|
||||
```text
|
||||
GET /api/search_conversations?q=<query>&limit=30
|
||||
```
|
||||
|
||||
| Parameter | Default | Description |
|
||||
| --- | --- | --- |
|
||||
| `q` | — (required) | Case-insensitive substring to search for. |
|
||||
| `limit` | `30` | Max results (max `100`). |
|
||||
|
||||
Each result includes a `match_field` (`name`, `prompt`, or `response`) and a `match_snippet` showing the matched text in context, in addition to the fields returned by `/api/get_conversations`.
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
title: Realtime Events & Notifications (SSE)
|
||||
description: Subscribe to DocsGPT's server-sent events channel for live notifications — ingestion progress, tool approvals, MCP OAuth completion — and reconnect to an in-flight chat answer.
|
||||
---
|
||||
|
||||
import { Callout } from 'nextra/components'
|
||||
|
||||
# Realtime Events & Notifications
|
||||
|
||||
DocsGPT pushes realtime updates to the browser over **Server-Sent Events (SSE)**. This is what powers the upload toasts, tool-approval prompts, and other live notifications in the UI. There are two channels:
|
||||
|
||||
- **User events** — `GET /api/events`: a per-user notification stream (ingestion progress, tool approvals, MCP OAuth completion, …).
|
||||
- **Chat reconnect** — `GET /api/messages/<message_id>/events`: resume an answer stream that was interrupted mid-generation.
|
||||
|
||||
<Callout type="info" emoji="ℹ️">
|
||||
Both channels require Redis (already a DocsGPT dependency). The publisher can be turned off instance-wide with `ENABLE_SSE_PUSH=false`.
|
||||
</Callout>
|
||||
|
||||
## User events channel
|
||||
|
||||
Open an SSE connection to receive notifications for the authenticated user:
|
||||
|
||||
```text
|
||||
GET /api/events
|
||||
Accept: text/event-stream
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
Each event is a JSON object with a `type` field, for example:
|
||||
|
||||
```text
|
||||
id: 1718900000000-0
|
||||
data: {"type":"source.ingest.queued","scope":{"id":"<source_id>"}, ...}
|
||||
```
|
||||
|
||||
Common event types:
|
||||
|
||||
| Type | Meaning |
|
||||
| --- | --- |
|
||||
| `source.ingest.queued` | A source ingestion task was enqueued (drives the upload toast). |
|
||||
| `mcp.oauth.completed` | An MCP server's OAuth handshake finished. |
|
||||
| tool-approval events | An agent is requesting approval to run a tool. |
|
||||
| `backlog.truncated` | The client's cursor slid off the retained backlog window — clear your cursor and refetch state. |
|
||||
|
||||
### Reconnecting and backlog replay
|
||||
|
||||
Events are journaled per user in a Redis Stream so a client that reconnects can catch up on what it missed. Send the last id you processed and DocsGPT replays everything after it:
|
||||
|
||||
```text
|
||||
GET /api/events
|
||||
Last-Event-ID: 1718900000000-0
|
||||
```
|
||||
|
||||
(You may also pass it as a `last_event_id` query parameter.) Each delivered event carries its own `id:`, so your cursor advances as you read. If you fall a long way behind, the snapshot is delivered across several reconnects rather than all at once.
|
||||
|
||||
A few bounded behaviors to be aware of:
|
||||
|
||||
- The backlog is capped at `EVENTS_STREAM_MAXLEN` entries (default 1000). If your `Last-Event-ID` is older than the oldest retained entry, you receive a `backlog.truncated` event — reset your cursor and refetch current state.
|
||||
- Each snapshot is capped at `EVENTS_REPLAY_MAX_PER_REQUEST` entries per request (default 200); reconnect to continue.
|
||||
- There is a per-user cap on simultaneous connections (`SSE_MAX_CONCURRENT_PER_USER`, default 8) and a windowed replay budget. Exceeding either returns **HTTP 429** — back off and retry.
|
||||
|
||||
## Chat answer reconnect
|
||||
|
||||
When an answer is streaming and the connection drops, resume it without losing the in-progress generation:
|
||||
|
||||
```text
|
||||
GET /api/messages/<message_id>/events
|
||||
```
|
||||
|
||||
This replays the message's events past your last-seen sequence number and tails the rest live. It is backed by the Postgres `message_events` journal (retained for `MESSAGE_EVENTS_RETENTION_DAYS`, default 14).
|
||||
|
||||
<Callout type="warning" emoji="⚠️">
|
||||
The chat reconnect endpoint is a native-async route served by the ASGI entrypoint. Under a plain `flask run` dev server it returns `404`; run the backend via the ASGI app (`uvicorn application.asgi:asgi_app`) or the production gunicorn uvicorn worker to use it. See the [Development Environment](/Deploying/Development-Environment) guide.
|
||||
</Callout>
|
||||
|
||||
## Settings
|
||||
|
||||
| Setting | Default | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `ENABLE_SSE_PUSH` | `true` | Master switch for the publisher and channel. |
|
||||
| `EVENTS_STREAM_MAXLEN` | `1000` | Per-user backlog cap (approximate). |
|
||||
| `SSE_KEEPALIVE_SECONDS` | `15` | Keepalive comment-frame cadence (keep below your proxy's idle timeout). |
|
||||
| `SSE_MAX_CONCURRENT_PER_USER` | `8` | Max simultaneous SSE connections per user (`0` disables the cap). |
|
||||
| `EVENTS_REPLAY_MAX_PER_REQUEST` | `200` | Max backlog entries per replay request. |
|
||||
| `EVENTS_REPLAY_BUDGET_REQUESTS_PER_WINDOW` | `30` | Per-user replay requests per window (`0` disables). |
|
||||
| `EVENTS_REPLAY_BUDGET_WINDOW_SECONDS` | `60` | Replay budget window length. |
|
||||
| `MESSAGE_EVENTS_RETENTION_DAYS` | `14` | Retention for the chat-stream `message_events` journal. |
|
||||
|
||||
<Callout type="info" emoji="ℹ️">
|
||||
Operators debugging delivery issues ("the toast never appeared", "the answer didn't reconnect") can follow the SSE notifications runbook in the repository at `docs/runbooks/sse-notifications.md`.
|
||||
</Callout>
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: OpenAI-Compatible API
|
||||
description: Connect any OpenAI-compatible client to DocsGPT Agents via /v1/chat/completions.
|
||||
description: Connect any OpenAI-compatible client to DocsGPT Agents via /v1/chat/completions — streaming, structured output, multimodal, tool calling, reasoning, and idempotent retries.
|
||||
---
|
||||
|
||||
import { Callout, Tabs } from 'nextra/components';
|
||||
@@ -58,7 +58,7 @@ Authenticate with `Authorization: Bearer <agent_api_key>`.
|
||||
|
||||
## Streaming
|
||||
|
||||
Set `"stream": true`. You'll receive SSE chunks with `choices[0].delta.content`. DocsGPT-specific events (sources, tool calls) arrive as extra frames with a `docsgpt` key — standard clients ignore them.
|
||||
Set `"stream": true`. You'll receive SSE chunks with `choices[0].delta.content`. DocsGPT-specific events (sources, tool calls) arrive as extra frames that carry a top-level `docsgpt` key on an otherwise-empty chunk — standard clients ignore them.
|
||||
|
||||
```python
|
||||
stream = client.chat.completions.create(
|
||||
@@ -70,6 +70,135 @@ for chunk in stream:
|
||||
print(chunk.choices[0].delta.content or "", end="", flush=True)
|
||||
```
|
||||
|
||||
## Sampling Parameters
|
||||
|
||||
Standard OpenAI sampling parameters are forwarded to the model. When omitted, the agent's configured defaults apply. Supported: `temperature`, `max_tokens` (or `max_completion_tokens`), `top_p`, `frequency_penalty`, `presence_penalty`, `stop`, `seed`.
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "docsgpt-agent",
|
||||
"messages": [{"role": "user", "content": "Write a haiku about search"}],
|
||||
"temperature": 0.2,
|
||||
"max_tokens": 256,
|
||||
"seed": 42
|
||||
}
|
||||
```
|
||||
|
||||
## Structured Output
|
||||
|
||||
You can force the model to return JSON matching a schema, using either the OpenAI `response_format` field or the `response_schema` convenience field.
|
||||
|
||||
<Tabs items={['response_format', 'response_schema']}>
|
||||
<Tabs.Tab>
|
||||
```json
|
||||
{
|
||||
"model": "docsgpt-agent",
|
||||
"messages": [{"role": "user", "content": "Extract the order id and total"}],
|
||||
"response_format": {
|
||||
"type": "json_schema",
|
||||
"json_schema": {
|
||||
"name": "order",
|
||||
"strict": true,
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"order_id": {"type": "string"},
|
||||
"total": {"type": "number"}
|
||||
},
|
||||
"required": ["order_id", "total"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab>
|
||||
```json
|
||||
{
|
||||
"model": "docsgpt-agent",
|
||||
"messages": [{"role": "user", "content": "Extract the order id and total"}],
|
||||
"response_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"order_id": {"type": "string"},
|
||||
"total": {"type": "number"}
|
||||
},
|
||||
"required": ["order_id", "total"]
|
||||
}
|
||||
}
|
||||
```
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
|
||||
- `response_format` follows OpenAI Structured Outputs. `strict` defaults to `true`; set `strict: false` to relax enforcement.
|
||||
- `response_format: {"type": "json_object"}` requests JSON without a fixed schema (the model is steered by the prompt).
|
||||
- `response_schema` is a DocsGPT convenience: pass a raw JSON Schema object (or a `{"schema": {...}}` wrapper) directly.
|
||||
|
||||
## Multimodal Input (text + images)
|
||||
|
||||
User messages may use OpenAI typed-content arrays with `image_url` parts. Images are forwarded to vision-capable models.
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "docsgpt-agent",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "text", "text": "What's in this screenshot?"},
|
||||
{"type": "image_url", "image_url": {"url": "https://example.com/shot.png"}}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Tool Calling (client-side, stateless)
|
||||
|
||||
You can register your own tools and execute them on the client. The flow is stateless — OpenAI clients that don't carry a `conversation_id` re-send the full message history each turn, and DocsGPT rebuilds the agent from it.
|
||||
|
||||
1. Send a request with a `tools` array.
|
||||
2. If the agent decides to call a tool, the response comes back with `finish_reason: "tool_calls"` and a `tool_calls` array (and `content: null`).
|
||||
3. Execute the tool(s) on your side, then **re-POST the full message history** with the assistant's `tool_calls` message followed by `role: "tool"` result messages.
|
||||
4. DocsGPT continues the run and returns the final answer.
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "docsgpt-agent",
|
||||
"messages": [
|
||||
{"role": "user", "content": "What's the weather in Paris?"},
|
||||
{"role": "assistant", "tool_calls": [
|
||||
{"id": "call_1", "type": "function",
|
||||
"function": {"name": "get_weather", "arguments": "{\"city\":\"Paris\"}"}}
|
||||
]},
|
||||
{"role": "tool", "tool_call_id": "call_1", "content": "18°C, clear"}
|
||||
],
|
||||
"tools": [ { "type": "function", "function": { "name": "get_weather", "...": "..." } } ]
|
||||
}
|
||||
```
|
||||
|
||||
## Reasoning
|
||||
|
||||
For models that emit reasoning ("thinking") tokens, the response surfaces them in a non-standard `reasoning_content` field (a `reasoning_content` delta when streaming). Standard clients ignore it; clients that understand it can display the model's thinking separately from the answer.
|
||||
|
||||
## Idempotent Retries
|
||||
|
||||
Add an `Idempotency-Key` header so a retried request returns the *stored first response* instead of re-running the agent (which would duplicate the answer and double-bill tokens).
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:7091/v1/chat/completions \
|
||||
-H "Authorization: Bearer your_agent_api_key" \
|
||||
-H "Idempotency-Key: 8f1c...unique-per-request" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"model":"docsgpt-agent","messages":[{"role":"user","content":"hi"}]}'
|
||||
```
|
||||
|
||||
- **Opt-in** — no header means today's behavior (every request runs).
|
||||
- **Non-streaming only** — streaming replay is not supported.
|
||||
- A completed key **replays the cached body** (and status) for **24 hours**.
|
||||
- A request with a key whose first attempt is **still in flight** returns **HTTP 409**.
|
||||
- Keys are scoped per agent and capped at **256 characters** (oversized keys are rejected).
|
||||
|
||||
## System Prompt Override
|
||||
|
||||
System messages are **dropped by default** — the agent's configured prompt is used. To allow callers to override it, enable **Allow prompt override** in the agent's Advanced settings.
|
||||
@@ -80,13 +209,30 @@ When an override is active, the agent's prompt template is replaced wholesale
|
||||
|
||||
## Conversation Persistence
|
||||
|
||||
Conversations are **always persisted** server-side, and the response includes
|
||||
`docsgpt.conversation_id`. They never appear in the agent owner's sidebar —
|
||||
`/v1` traffic is stored hidden, so external clients can't clutter the owner's
|
||||
conversation list.
|
||||
Conversations are **always persisted** server-side, and the response includes `docsgpt.conversation_id`. They never appear in the agent owner's sidebar — `/v1` traffic is stored hidden, so external clients can't clutter the owner's conversation list.
|
||||
|
||||
The legacy `{ "docsgpt": { "save_conversation": true } }` opt-in from older
|
||||
releases (when `/v1` was stateless by default) is deprecated and ignored.
|
||||
Stateless tool continuations (no `conversation_id`, e.g. opencode) skip persistence by default to avoid writing orphan rows; set `docsgpt.persist` to override. The legacy `docsgpt.save_conversation` flag from older releases is deprecated and ignored.
|
||||
|
||||
## DocsGPT Extension Fields
|
||||
|
||||
DocsGPT adds an optional `docsgpt` object to both requests and responses for features outside the OpenAI schema.
|
||||
|
||||
**Request** (`docsgpt.*`):
|
||||
|
||||
| Field | Description |
|
||||
| --- | --- |
|
||||
| `attachments` | List of attachment IDs to include as context for this turn. |
|
||||
| `persist` | Force-enable/disable conversation persistence (mainly for stateless tool continuations). |
|
||||
|
||||
**Response** (`docsgpt.*`):
|
||||
|
||||
| Field | Description |
|
||||
| --- | --- |
|
||||
| `conversation_id` | Server-side conversation ID for this exchange. |
|
||||
| `sources` | RAG sources used to answer. |
|
||||
| `tool_calls` | Completed tool-call results from the run. |
|
||||
|
||||
When streaming, these arrive on otherwise-empty chunks that carry a top-level `docsgpt` key, so strict OpenAI clients still validate each frame.
|
||||
|
||||
## When to Use Native Endpoints Instead
|
||||
|
||||
|
||||
@@ -88,6 +88,28 @@ Send an HTTP `POST` request to the agent's unique webhook URL with the required
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
|
||||
### Triggering with GET (query parameters)
|
||||
|
||||
The webhook listener also accepts `GET` requests, mapping the query string to the payload. This is handy for systems that can only issue a simple `GET` (for example a "ping this URL" integration):
|
||||
|
||||
```bash
|
||||
curl "http://localhost:7091/api/webhooks/agents/your_webhook_token?question=Your+message+to+agent"
|
||||
```
|
||||
|
||||
As with `POST`, the response is a JSON object containing the `task_id`.
|
||||
|
||||
### Preventing duplicate triggers (Idempotency-Key)
|
||||
|
||||
To make a trigger safe to retry, send an optional `Idempotency-Key` header. If the same key is seen again within 24 hours, DocsGPT does not enqueue a second task — it returns the **original** `task_id` instead. This prevents a retried or double-fired webhook from running the agent twice.
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:7091/api/webhooks/agents/your_webhook_token \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Idempotency-Key: order-4567-created" \
|
||||
-d '{"question": "Your message to agent"}'
|
||||
```
|
||||
|
||||
### Step 2: Poll for the Result
|
||||
|
||||
Once you have the `task_id`, periodically send a `GET` request to the `/api/task_status` endpoint until the task `status` is `SUCCESS` or `FAILURE`.
|
||||
|
||||
Reference in new issue
Block a user