Skip to content
Merged
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
28 changes: 23 additions & 5 deletions document/en/api/overview.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# API Overview

> Last updated: 2026-07-28
> Last updated: August 12, 2026

The HugAgentOS backend is a FastAPI application; all business endpoints live under the `/v1/*` prefix. In a production deployment, Nginx strips the `/api/` prefix before forwarding to the backend (see `src/frontend/default.conf.template`), so **the full browser-facing path is `/api/v1/...`**, while hitting the backend container directly uses `/v1/...`. Examples in this document use the local development address `http://localhost:3000/api`.

Expand Down Expand Up @@ -112,7 +112,8 @@ The request body is a `ChatRequest` (`src/backend/api/schemas.py`): `chat_id` an
| `tool_call_start` | The model starts constructing a tool call | `tool_name`, `tool_display_name`, `tool_id` |
| `tool_call_delta` | Incremental tool-argument JSON (batched by the backend) | `tool_name`, `tool_id`, `arguments_delta` |
| `tool_call` | Arguments are complete and execution is about to start | `tool_name`, `tool_display_name`, `tool_args`, `tool_id`, `subagent_name?` |
| `tool_result` | Tool returns | `tool_name`, `result` (JSON), `tool_id`, `citations` (citation items) |
| `tool_result` | Tool returns | `tool_name`, `result` (JSON), `tool_id`, `status`, `citations` (citation items) |
| `steer_applied` | A follow-up entered the context at a safe ReAct boundary | `steer_id`, `message`, `message_id`, `chat_id` |
| `tool_pending` | Waiting fallback when the provider exposes no parseable deltas | `reason` (e.g. `llm_buffering`) |
| `file_confirm` | A tool is suspended awaiting user confirmation of a "My Space" write | `confirm_id`, `op`, `logical_path`, `message`, `expired`; the stream stays open — the user confirms out-of-band via `POST /v1/chats/{chat_id}/file-confirm` and the tool resumes |
| `batch_confirm` | A batch-execution plan awaits user confirmation | `plan_id`, `total`, `preview`, `default_template`, `placeholder_keys`; confirm via `POST /v1/batch/{plan_id}/confirm` |
Expand Down Expand Up @@ -145,7 +146,7 @@ data: [DONE]

`[ref:tool_name-N]` markers in the answer text are parsed into `citations` items by `orchestration/citations.py`; the frontend renders them as citation badges (see [Chat module](../modules/chat.md)).

### Resume and cancel
### Resume, Steer, and cancel

The answer is generated by a background run; the SSE connection merely *follows* it — disconnecting does not stop generation:

Expand All @@ -157,9 +158,26 @@ curl -N "http://localhost:3000/api/v1/chats/stream/run_9f8e7d?from_offset=0" \
# Cancel generation
curl -X POST http://localhost:3000/api/v1/chat-runs/run_9f8e7d/cancel \
-H "Authorization: Bearer sk-jx-xxxxxxxx"

# Add a plain-text instruction at the next safe ReAct boundary
curl -X POST http://localhost:3000/api/v1/chat-runs/run_9f8e7d/steer \
-H "Authorization: Bearer sk-jx-xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"steer_id":"steer_001","message":"Do not download it. Compare both options instead."}'
```

Resume validates run ownership: a non-owner gets 403, a missing run gets 404. `GET /v1/chats/{chat_id}/active-run` reports whether a chat currently has a run in progress (the frontend uses this to reconnect after a page refresh). A run silent for longer than `CHAT_RUN_INACTIVITY_TIMEOUT_SEC` (default 600 s) is considered dead and terminated.
Resume, Steer, and cancel validate run ownership: a non-owner gets 403, and a
missing run gets 404. Steer accepts only an active regular-chat run. Redis
hands the instruction to the worker. If a tool is running, the worker injects
the instruction after that tool result enters the context and before the next
model call. If the next tool batch hasn't started, the worker interrupts the
old call before injecting the instruction. Both paths confirm delivery with a
`steer_applied` event. `DELETE /v1/chat-runs/{run_id}/steer/{steer_id}`
withdraws an instruction that hasn't been consumed.
`GET /v1/chats/{chat_id}/active-run` reports whether a chat currently has a run
in progress, which the frontend uses to reconnect after a page refresh. A run
silent for longer than `CHAT_RUN_INACTIVITY_TIMEOUT_SEC` (default 600 seconds)
is considered dead and terminated.

### Other SSE endpoints

Expand All @@ -186,7 +204,7 @@ Auth column legend: "User" = session cookie or personal API key (`get_current_us
| Group | Module (`api/routes/v1/`) | Prefix | Representative endpoints | Auth |
|---|---|---|---|---|
| Chat & messages | `chats.py` | `/v1/chats` | `POST /stream` (SSE), `GET /stream/{run_id}` (resume), `POST /send` (non-streaming), `GET /`, `GET /{chat_id}/messages`, `POST /{chat_id}/share` | User |
| Chat & messages | `chat_runs.py` | `/v1/chat-runs` | `POST /{run_id}/cancel` | User |
| Chat & messages | `chat_runs.py` | `/v1/chat-runs` | `POST /{run_id}/cancel`, `POST /{run_id}/steer`, `DELETE /{run_id}/steer/{steer_id}` | User |
| Chat & messages | `chat_shares.py` | `/v1/chat-shares` | `POST /`, `GET /{share_id}`, `POST /{share_id}/revoke` | User |
| Chat & messages | `summary.py` | `/v1/summary` | `POST /` (chat title summarization) | User |
| Chat & messages | `classify.py` | `/v1/classify` | `POST /` (business-topic classification) | User |
Expand Down
33 changes: 30 additions & 3 deletions document/en/modules/chat.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,37 @@ Every sent message creates a `ChatRun` and a background task (`orchestration/cha
| Resume after refresh / disconnect | `GET /v1/chats/stream/{run_id}?from_offset=N` |
| Probe for an in-flight run | `GET /v1/chats/{chat_id}/active-run` |
| Cancel a run (kills the background task) | `POST /v1/chat-runs/{run_id}/cancel` |
| Add an instruction at the next safe ReAct boundary | `POST /v1/chat-runs/{run_id}/steer` |
| Withdraw an instruction that hasn't taken effect | `DELETE /v1/chat-runs/{run_id}/steer/{steer_id}` |

Defensive machinery: a `: heartbeat` SSE comment line every 15 silent seconds (keeps nginx `proxy_read_timeout` and other proxies from cutting the stream); an inactivity watchdog fails the run if the workflow produces no chunk for 600 s (`CHAT_RUN_INACTIVITY_TIMEOUT_SEC`); a periodic reaper collects over-age running runs; `recover_orphan_runs()` cleans up leftovers at startup.

### Mid-run follow-ups, Steer, and the stop shortcut

While a regular chat is generating, the composer continues accepting the next
message. Sending it creates a queued card above the composer. You can edit the
card from its more menu or delete it. If you don't select **Steer**, the client
sends the message as the next turn after the current answer finishes.

When you select **Steer**, Redis hands the plain-text instruction to the active
run. If the instruction arrives while a tool is running, `SteerMiddleware`
atomically consumes it after that tool result enters the context and before the
next model call, appends the real user message, and lets the model replan. If
the instruction arrives earlier, the middleware interrupts the old tool call
before it starts and enters the same replanning flow. A `steer_applied` SSE
event confirms delivery. Messages containing attachments, skills, plugins, or
sub-agents wait and send normally after the current answer. Pressing `Esc`
cancels the run for the chat visible on the current page. If a card editor or
dialog already consumes `Esc`, it doesn't stop the run.

### Agent construction highlights (core/llm/agent_factory.py)

`create_agent_executor()` is the shared factory for every mode (main chat, plan, batch, sub-agents, automation):

- **MCP tools**: after the three-layer filter of catalog + per-user overrides + request context (see [Capability Center](catalog.md)), stable servers reuse the process-level connection pool (`core/llm/mcp_pool.py`); per-request servers (e.g. `retrieve_dataset_content`, which needs per-request HTTP headers) are spawned fresh; the user's self-added private MCP servers are merged in with owner isolation.
- **Skills**: registered as AgentScope Agent Skills via `core/agent_skills/loader.py`, with `view_text_file` allow-listed to read SKILL.md files (see [Agent Skills](agent-skills.md)).
- **File / sandbox tools**: `bash`, `sandbox_put_artifact`, `sandbox_get_artifact` are always registered; Read/Edit/Write/Glob/Grep/Delete/Move/mkdir plus the MySpace tools are gated by `CODE_CAPABILITY_ENABLED` and share one `ReadStateTracker` to keep the "must Read before Edit" invariant.
- **Middlewares** (onion model, `core/llm/middlewares.py`): `DynamicModelMiddleware` (switches the model per chat_mode, see [Model Providers](model-providers.md)), `FileContextMiddleware` (injects uploaded/historical file context), `WorkspacePinHintMiddleware`, `GoalAnchorReminderMiddleware`, `FinishPinGuardMiddleware`.
- **Middlewares** (onion model, `core/llm/middlewares.py`): `DynamicModelMiddleware` (switches the model per chat_mode, see [Model Providers](model-providers.md)), `FileContextMiddleware` (injects uploaded/historical file context), `SteerMiddleware` (injects follow-ups after tool results and before the next reasoning round), `WorkspacePinHintMiddleware`, `GoalAnchorReminderMiddleware`, `FinishPinGuardMiddleware`.
- **Context compression**: `ContextConfig(trigger_ratio=0.6, tool_result_limit=20000)` plus a structured Chinese compression prompt designed to produce a *resumable ReAct workflow* summary; if the compression call itself fails, `JxOpenAIChatModel.generate_structured_output` returns an L3 synthetic summary so the reply never crashes.
- **Permissions**: every registered tool gets a native `PermissionRule(ALLOW)` seed, preserving AgentScope's built-in dangerous-operation checks (no blanket BYPASS).
- **Iteration caps**: main agent defaults to `max_iters=50`, isolated sub-agents to 10.
Expand All @@ -69,7 +89,8 @@ Defensive machinery: a `: heartbeat` SSE comment line every 15 silent seconds (k
| `tool_call_start` | Tool-call construction starts; the frontend opens one card by stable ID | `tool_name`, `tool_display_name`, `tool_id` |
| `tool_call_delta` | Incremental argument JSON appended to the same card | `tool_name`, `tool_id`, `arguments_delta` |
| `tool_call` | Arguments are complete and execution is about to start | `tool_name`, `tool_display_name`, `tool_args`, `tool_id`, plus `subagent_name` for sub-agent calls |
| `tool_result` | Tool invocation result | `tool_name`, `result`, `tool_id`, `citations[]` |
| `tool_result` | Tool invocation result | `tool_name`, `result`, `tool_id`, `status`, `citations[]` |
| `steer_applied` | A mid-run instruction entered the ReAct context | `steer_id`, `message`, `message_id`, `chat_id` |
| `subagent_event` | Child execution details nested under the parent `call_subagent` card | `parent_tool_id`, `sub_type`, `agent_name`, plus child tool or content fields |
| `ontology_activation` / `ontology_gate` / `ontology_review` | Ontology-governance state, separate from model reasoning | workflow activation, gate decision, and committee status or verdict |
| `tool_pending` | Waiting fallback when the provider exposes no parseable argument deltas | `reason` |
Expand Down Expand Up @@ -112,6 +133,12 @@ event, the frontend replaces the body in place, and the database stores only
the reviewed final answer. It persists `ontology_governance` with the assistant
message so the module remains available after a history refresh.

History replay recognizes both reasoning protocols. Inline-reasoning models may
emit `reasoning</think>body`, while the backend normalizes a structured reasoning
field to `<think>reasoning</think>body`. The frontend places reasoning and tool
calls in one process area and renders all visible body text as one continuous
Markdown block. History rendering doesn't split the body at character offsets.

## Citation system (Evidence Anchors)

Citations make every fact in the answer traceable back to a specific tool result. Numbering authority belongs to a single backend source of truth — the model only **copies** ids, never computes them. The chain has four segments:
Expand Down Expand Up @@ -280,4 +307,4 @@ The same orchestration foundation also powers: response regeneration (`POST /v1/
| Oversized-result offloading | `src/backend/core/llm/offloader.py` |
| Chat sharing | `src/backend/api/routes/v1/chat_shares.py` |
| Follow-up generation | `src/backend/orchestration/followups.py` |
| Frontend stream parsing | `src/frontend/src/hooks/chatStream.ts` |
| Frontend stream parsing / follow-up queue | `src/frontend/src/hooks/chatStream.ts`, `useStreaming.ts`, `components/chat/QueuedMessageCard.tsx` |
17 changes: 12 additions & 5 deletions document/zh-CN/api/overview.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# API 总览

> 最后更新:2026-07-28
> 最后更新:2026-08-12

HugAgentOS 后端是一个 FastAPI 应用,所有业务接口挂在 `/v1/*` 前缀下。生产部署中 Nginx 把 `/api/` 前缀剥掉后转发给后端(见 `src/frontend/default.conf.template`),因此**浏览器侧的完整路径是 `/api/v1/...`**,直接访问后端容器则是 `/v1/...`。本文示例统一使用本地开发地址 `http://localhost:3000/api`。

Expand Down Expand Up @@ -112,7 +112,8 @@ curl -N http://localhost:3000/api/v1/chats/stream \
| `tool_call_start` | 模型开始构造工具调用 | `tool_name`、`tool_display_name`、`tool_id` |
| `tool_call_delta` | 工具参数 JSON 增量(后端合批后下发) | `tool_name`、`tool_id`、`arguments_delta` |
| `tool_call` | 参数完整、即将执行工具 | `tool_name`、`tool_display_name`、`tool_args`、`tool_id`、`subagent_name?` |
| `tool_result` | 工具返回结果 | `tool_name`、`result`(JSON)、`tool_id`、`citations`(引用项列表) |
| `tool_result` | 工具返回结果 | `tool_name`、`result`(JSON)、`tool_id`、`status`、`citations`(引用项列表) |
| `steer_applied` | 追加指令已在安全 ReAct 边界注入 | `steer_id`、`message`、`message_id`、`chat_id` |
| `tool_pending` | 提供商未暴露可解析增量时的等待兜底 | `reason`(如 `llm_buffering`) |
| `file_confirm` | 工具挂起等待用户确认「我的空间」写操作 | `confirm_id`、`op`、`logical_path`、`message`、`expired`;流不结束,用户带外 `POST /v1/chats/{chat_id}/file-confirm` 后续跑 |
| `batch_confirm` | 批量执行计划等待用户确认 | `plan_id`、`total`、`preview`、`default_template`、`placeholder_keys`;确认走 `POST /v1/batch/{plan_id}/confirm` |
Expand Down Expand Up @@ -145,7 +146,7 @@ data: [DONE]

正文中的 `[ref:tool_name-N]` 引用标记由 `orchestration/citations.py` 解析为 `citations` 项,前端据此渲染角标(见 [对话模块](../modules/chat.md))。

### 续播与取消
### 续播、Steer 与取消

回答在后台 run 中执行,SSE 只是「跟随」——断开连接不会终止生成:

Expand All @@ -157,9 +158,15 @@ curl -N "http://localhost:3000/api/v1/chats/stream/run_9f8e7d?from_offset=0" \
# 主动取消生成
curl -X POST http://localhost:3000/api/v1/chat-runs/run_9f8e7d/cancel \
-H "Authorization: Bearer sk-jx-xxxxxxxx"

# 在下一次安全 ReAct 边界追加纯文本指令
curl -X POST http://localhost:3000/api/v1/chat-runs/run_9f8e7d/steer \
-H "Authorization: Bearer sk-jx-xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"steer_id":"steer_001","message":"先不要下载,改为比较两个方案"}'
```

续播校验 run 归属:非属主返回 403、run 不存在返回 404。`GET /v1/chats/{chat_id}/active-run` 可查询某会话当前是否有进行中的 run(前端刷新页面后据此重连)。run 静默超过 `CHAT_RUN_INACTIVITY_TIMEOUT_SEC`(默认 600 秒)会被判定为僵死并终止。
续播、Steer 和取消都会校验 run 归属:非属主返回 403、run 不存在返回 404。Steer 只支持进行中的普通对话 run;指令通过 Redis 交给执行进程。若工具正在执行,指令会在该工具结果进入上下文后、下一轮模型推理前注入;若下一批工具尚未开始,则先中止旧调用再注入。两种路径都以 `steer_applied` 事件确认。`DELETE /v1/chat-runs/{run_id}/steer/{steer_id}` 可撤回尚未被消费的指令。`GET /v1/chats/{chat_id}/active-run` 可查询某会话当前是否有进行中的 run(前端刷新页面后据此重连)。run 静默超过 `CHAT_RUN_INACTIVITY_TIMEOUT_SEC`(默认 600 秒)会被判定为僵死并终止。

### 其他 SSE 端点

Expand All @@ -186,7 +193,7 @@ curl -X POST http://localhost:3000/api/v1/chat-runs/run_9f8e7d/cancel \
| 分组 | 模块(`api/routes/v1/`) | 前缀 | 代表端点 | 鉴权 |
|---|---|---|---|---|
| 会话与消息 | `chats.py` | `/v1/chats` | `POST /stream`(SSE)、`GET /stream/{run_id}`(续播)、`POST /send`(非流式)、`GET /`、`GET /{chat_id}/messages`、`POST /{chat_id}/share` | 用户 |
| 会话与消息 | `chat_runs.py` | `/v1/chat-runs` | `POST /{run_id}/cancel` | 用户 |
| 会话与消息 | `chat_runs.py` | `/v1/chat-runs` | `POST /{run_id}/cancel`、`POST /{run_id}/steer`、`DELETE /{run_id}/steer/{steer_id}` | 用户 |
| 会话与消息 | `chat_shares.py` | `/v1/chat-shares` | `POST /`、`GET /{share_id}`、`POST /{share_id}/revoke` | 用户 |
| 会话与消息 | `summary.py` | `/v1/summary` | `POST /`(会话标题摘要) | 用户 |
| 会话与消息 | `classify.py` | `/v1/classify` | `POST /`(业务主题分类) | 用户 |
Expand Down
Loading
Loading