2026-03-04 19:17:16 +08:00
|
|
|
//
|
|
|
|
|
// Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
|
|
|
|
|
//
|
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
|
//
|
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
//
|
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
|
// limitations under the License.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
package router
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
|
2026-06-23 16:21:46 +08:00
|
|
|
"ragflow/internal/common"
|
2026-03-04 19:17:16 +08:00
|
|
|
"ragflow/internal/handler"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Router struct {
|
2026-06-08 21:38:15 +08:00
|
|
|
authHandler *handler.AuthHandler
|
|
|
|
|
userHandler *handler.UserHandler
|
|
|
|
|
tenantHandler *handler.TenantHandler
|
|
|
|
|
documentHandler *handler.DocumentHandler
|
|
|
|
|
datasetsHandler *handler.DatasetsHandler
|
|
|
|
|
systemHandler *handler.SystemHandler
|
|
|
|
|
chunkHandler *handler.ChunkHandler
|
|
|
|
|
llmHandler *handler.LLMHandler
|
|
|
|
|
chatHandler *handler.ChatHandler
|
2026-06-22 18:16:15 +08:00
|
|
|
chatChannelHandler *handler.ChatChannelHandler
|
2026-06-25 19:25:55 +08:00
|
|
|
langfuseHandler *handler.LangfuseHandler
|
2026-06-18 18:07:27 +08:00
|
|
|
openaiChatHandler *handler.OpenAIChatHandler
|
2026-06-08 21:38:15 +08:00
|
|
|
chatSessionHandler *handler.ChatSessionHandler
|
|
|
|
|
connectorHandler *handler.ConnectorHandler
|
|
|
|
|
searchHandler *handler.SearchHandler
|
|
|
|
|
fileHandler *handler.FileHandler
|
|
|
|
|
memoryHandler *handler.MemoryHandler
|
|
|
|
|
mcpHandler *handler.MCPHandler
|
2026-07-02 09:45:01 +08:00
|
|
|
mcpServerHandler *handler.MCPServerHandler
|
2026-06-08 21:38:15 +08:00
|
|
|
skillSearchHandler *handler.SkillSearchHandler
|
|
|
|
|
providerHandler *handler.ProviderHandler
|
|
|
|
|
agentHandler *handler.AgentHandler
|
|
|
|
|
searchBotHandler *handler.SearchBotHandler
|
|
|
|
|
difyRetrievalHandler *handler.DifyRetrievalHandler
|
|
|
|
|
pluginHandler *handler.PluginHandler
|
|
|
|
|
modelHandler *handler.ModelHandler
|
2026-06-15 11:19:56 +08:00
|
|
|
fileCommitHandler *handler.FileCommitHandler
|
feat(agent): Go port — canvas engine, 22 components, DSL v2, 13 endpoints (#15952)
Ports the agent canvas subsystem from Python to Go.
## What's included
### Canvas Engine (Phase 0/1)
- State engine, scheduler, variable resolver, Redis checkpoint store,
cancel protocol
- **209 tests** across canvas / component / io packages
### 22 Components (P0–P4)
| Tier | Components |
|---|---|
| P0 T1+T2+T3 | LLM, Agent, ExitLoop, Switch, Categorize, Begin,
Message, Invoke |
| P1 T3 | VariableAggregator, VariableAssigner, StringTransform,
ListOperations, DataOperations |
| P2 T3 | Iteration, IterationItem, Loop, LoopItem |
| P3 T3 | UserFillUp, Fillup |
| P4 T5 | Browser, ExcelProcessor, DocsGenerator |
### DSL v2 Schema (Phase 2.5)
- Typed v2 in-memory model with v1-to-v2 auto-detect converter
- v1 legacy field stripping per plan §2.11.7
### HTTP Endpoints & Bug Fixes (Plans PR1–PR3)
- **DELETE SQL bug fix**: gorm v2 `Where("id = ?", id).Delete(...)`
pattern
- **CreateAgent validation**: title/DSL required, duplicate check, 103
envelope
- **13 new endpoints**: templates, prompts, tags, sessions CRUD,
chat/completions (SSE + non-stream stubs), rerun, test_db_connection,
logs, webhook/logs
- **756 Go unit tests** (745 → 756, +18)
- **17 → 0 Python integration test failures** (test_agents.py +
test_session_management/)
### Tools
21 eino tools: HTTPHelper, search tools, financial/data tools, mandatory
stubs
### Infrastructure
OTel observability, NATS message queue, DeepDoc gRPC client, SSRF
guards, IDOR mitigation
2026-06-12 22:58:28 +08:00
|
|
|
adminRuntimeHandler *handler.AdminRuntimeHandler
|
feat[Go]: port agent attachment download, chatbot + agentbot completion/info endpoints from Python (#16405)
## Summary
Ports five Python agent APIs to Go under the v1 Gin router:
- `GET /api/v1/agents/attachments/<attachment_id>/download`
- `POST /api/v1/chatbots/<dialog_id>/completions` (SSE)
- `GET /api/v1/chatbots/<dialog_id>/info`
- `POST /api/v1/agentbots/<agent_id>/completions` (SSE)
- `GET /api/v1/agentbots/<agent_id>/inputs`
Mirrors the existing Python wire shape (`{code, message,
data:{answer,reference,...}}` per Python `canvas_service.completion`) so
the iframe SDK and existing JS widgets keep working.
## Behavioural parity with Python
| # | Concern | How it's met |
|---|---------|--------------|
| R0 | Bot routes must not require regular user session | Routes mount
on `apiNoAuth` (router.go:198-202), with `BetaAuthMiddleware` only |
| R3 | Two SSE formats in Go drift | F2: `AgentChatCompletions` and
`AgentbotCompletion` share `service.WriteChatbotRunEvent` |
| R7 | `GetBySessionID` returns `(nil, nil)` on miss | Defensive
nil-check before `session.UserID != tenantID` |
| R8 | Begin component name vs ID | `FindBeginComponentID` resolves name
→ ID first, then `ExtractComponentInputForm(dsl, beginID)` |
| R9 | Defensive PromptConfig parsing | `stringFromMap` helper used for
`prologue` and `tavily_api_key` |
| R10 | `BetaAuthMiddleware` Bearer-prefix pre-filter | Removed —
`GetUserByToken` is called unconditionally, falls back to
`GetUserByBetaAPIToken` |
| F8 | Multi-turn chatbot history | `ChatbotCompletion` reads prior
turns from `session.Message`, appends user turn, calls LLM, persists new
pair via new `API4ConversationDAO.Update` |
| F9 | UUID gate stricter than plan | Removed — only `filepath.Base` +
CR/LF/quote header sanitization remains |
| H2 | Defence-in-depth IDOR | `AgentbotCompletion` calls `loadCanvas`
before delegating to `RunAgent` |
| M2 | SSE error leakage | `WriteChatbotFrame` emits generic `"an
internal error occurred"`; real error logged via `common.Error` |
## Verification
```bash
$ go vet ./... # clean (only pre-existing issues)
$ go build ./... # success
$ go test ./internal/handler/ ./internal/service/ ./internal/agent/dsl/ ./internal/common/ ./internal/dao/
ok ragflow/internal/handler 0.617s
ok ragflow/internal/service 1.729s
ok ragflow/internal/agent/dsl 0.008s
ok ragflow/internal/common 0.087s
ok ragflow/internal/dao 0.083s
```
1199 tests pass across 5 packages.
## Known follow-ups (out of scope for this PR)
- **F1**: token-level streaming in `ChatbotCompletion` (currently emits
one frame per turn)
- **F3**: per-route `auth_types` attribute in Go (currently applied via
route group middleware)
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-06-27 16:52:21 +08:00
|
|
|
botHandler *handler.BotHandler
|
2026-07-05 20:43:52 +08:00
|
|
|
componentsHandler *handler.ComponentsHandler
|
2026-03-04 19:17:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewRouter create router
|
|
|
|
|
func NewRouter(
|
2026-03-11 11:23:13 +08:00
|
|
|
authHandler *handler.AuthHandler,
|
2026-03-04 19:17:16 +08:00
|
|
|
userHandler *handler.UserHandler,
|
|
|
|
|
tenantHandler *handler.TenantHandler,
|
|
|
|
|
documentHandler *handler.DocumentHandler,
|
2026-03-19 20:48:32 +08:00
|
|
|
datasetsHandler *handler.DatasetsHandler,
|
2026-03-04 19:17:16 +08:00
|
|
|
systemHandler *handler.SystemHandler,
|
|
|
|
|
chunkHandler *handler.ChunkHandler,
|
|
|
|
|
llmHandler *handler.LLMHandler,
|
|
|
|
|
chatHandler *handler.ChatHandler,
|
2026-06-22 18:16:15 +08:00
|
|
|
chatChannelHandler *handler.ChatChannelHandler,
|
2026-06-25 19:25:55 +08:00
|
|
|
langfuseHandler *handler.LangfuseHandler,
|
2026-03-04 19:17:16 +08:00
|
|
|
chatSessionHandler *handler.ChatSessionHandler,
|
|
|
|
|
connectorHandler *handler.ConnectorHandler,
|
|
|
|
|
searchHandler *handler.SearchHandler,
|
|
|
|
|
fileHandler *handler.FileHandler,
|
2026-03-27 09:49:50 +08:00
|
|
|
memoryHandler *handler.MemoryHandler,
|
2026-05-27 22:43:21 -10:00
|
|
|
mcpHandler *handler.MCPHandler,
|
2026-07-02 09:45:01 +08:00
|
|
|
mcpServerHandler *handler.MCPServerHandler,
|
2026-04-30 12:36:03 +08:00
|
|
|
skillSearchHandler *handler.SkillSearchHandler,
|
2026-03-31 18:42:12 +08:00
|
|
|
providerHandler *handler.ProviderHandler,
|
2026-05-28 05:40:54 -06:00
|
|
|
agentHandler *handler.AgentHandler,
|
feat: implement POST /api/v1/searchbots/retrieval_test (#15710)
## What problem does this PR solve?
Implements `POST /api/v1/searchbots/retrieval_test` in the Go API
server, aligning with the Python `bot_api.py` counterpart. Also applies
security hardening and consistency fixes discovered during CTO-level
code review:
- **Missing endpoint**: `retrieval_test` was not available in Go,
requiring Python fallback
- **Security**: Both `chunkHandler` and `searchBotHandler` leaked
`err.Error()` to API consumers
- **Python alignment**: Default values, empty question handling, and
`top_k <= 0` validation differed from Python behavior
- **Test gaps**: `chunkHandler.RetrievalTest` had zero unit tests;
several edge cases uncovered
## Type of change
- [x] New Feature (non-breaking change which adds functionality)
- [x] Bug Fix (non-breaking change which fixes an issue)
- [x] Refactoring
## Summary
### New Endpoint
- `POST /api/v1/searchbots/retrieval_test` — retrieval test with full
field support (page, size, top_k, use_kg, cross_languages, keyword,
similarity_threshold, vector_similarity_weight)
### New Type
- `common.StringSlice` — JSON type that accepts both `"kb1"` and
`["kb1", "kb2"]`, matching Python API flexibility
### Security
- Both `searchBotHandler` and `chunkHandler` now use `common.Warn()` +
generic error messages instead of leaking `err.Error()` to API consumers
- All error responses include consistent `"data": nil` shape
- `chunkHandler.RetrievalTest` uses interface-based DI (`chunkService`)
to enable testability
### Python Alignment
- Handler-level defaults align with Python `bot_api.py` (page=1,
size=30, top_k=1024, similarity_threshold=0.0,
vector_similarity_weight=0.3)
- `top_k <= 0` validation matching Python behavior
- Empty/whitespace question returns 200 + empty result (matches
`chunk_api.py`)
- `chunkHandler` `Datasets` field uses `common.StringSlice` for
string-or-array flexibility
### Refactoring
- `ChunkServiceIface` → `ChunkRetriever`, `chunkSvcIface` →
`chunkService` (Go-conventional naming)
- Extracted `applyRetrievalDefaults`, `toRetrievalServiceRequest` from
handler body
- Regex moved to package-level var in `parseRelatedQuestions`
- `service.RetrievalTestRequest.Datasets` type changed to
`common.StringSlice`
- `chunkHandler` now uses consumer-side interface for DI
### Tests
- 37 unit tests across both handlers: auth, validation, defaults,
StringSlice edge cases, empty/whitespace KbID, service errors, JSON
format, `top_k <= 0`, field mapping verification
## Files Changed
| File | Change |
|------|--------|
| `cmd/server_main.go` | Wire new handler + chunkService +
difyRetrievalHandler |
| `internal/common/json_types.go` | New StringSlice type |
| `internal/common/json_types_test.go` | StringSlice tests |
| `internal/handler/chunk.go` | Interface-based DI, security, Python
alignment, defaults |
| `internal/handler/chunk_test.go` | New — 9 comprehensive tests |
| `internal/handler/searchbot.go` | New endpoint + refactoring + `top_k
<= 0` validation |
| `internal/handler/searchbot_test.go` | 18 tests covering all edge
cases |
| `internal/router/router.go` | Register new route +
difyRetrievalHandler |
| `internal/service/chunk.go` | Datasets type → StringSlice, Question
binding relaxed |
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 16:16:56 +08:00
|
|
|
searchBotHandler *handler.SearchBotHandler,
|
2026-06-07 20:53:19 -07:00
|
|
|
difyRetrievalHandler *handler.DifyRetrievalHandler,
|
|
|
|
|
pluginHandler *handler.PluginHandler,
|
2026-06-08 21:38:15 +08:00
|
|
|
modelHandler *handler.ModelHandler,
|
2026-06-15 11:19:56 +08:00
|
|
|
fileCommitHandler *handler.FileCommitHandler,
|
feat(agent): Go port — canvas engine, 22 components, DSL v2, 13 endpoints (#15952)
Ports the agent canvas subsystem from Python to Go.
## What's included
### Canvas Engine (Phase 0/1)
- State engine, scheduler, variable resolver, Redis checkpoint store,
cancel protocol
- **209 tests** across canvas / component / io packages
### 22 Components (P0–P4)
| Tier | Components |
|---|---|
| P0 T1+T2+T3 | LLM, Agent, ExitLoop, Switch, Categorize, Begin,
Message, Invoke |
| P1 T3 | VariableAggregator, VariableAssigner, StringTransform,
ListOperations, DataOperations |
| P2 T3 | Iteration, IterationItem, Loop, LoopItem |
| P3 T3 | UserFillUp, Fillup |
| P4 T5 | Browser, ExcelProcessor, DocsGenerator |
### DSL v2 Schema (Phase 2.5)
- Typed v2 in-memory model with v1-to-v2 auto-detect converter
- v1 legacy field stripping per plan §2.11.7
### HTTP Endpoints & Bug Fixes (Plans PR1–PR3)
- **DELETE SQL bug fix**: gorm v2 `Where("id = ?", id).Delete(...)`
pattern
- **CreateAgent validation**: title/DSL required, duplicate check, 103
envelope
- **13 new endpoints**: templates, prompts, tags, sessions CRUD,
chat/completions (SSE + non-stream stubs), rerun, test_db_connection,
logs, webhook/logs
- **756 Go unit tests** (745 → 756, +18)
- **17 → 0 Python integration test failures** (test_agents.py +
test_session_management/)
### Tools
21 eino tools: HTTPHelper, search tools, financial/data tools, mandatory
stubs
### Infrastructure
OTel observability, NATS message queue, DeepDoc gRPC client, SSRF
guards, IDOR mitigation
2026-06-12 22:58:28 +08:00
|
|
|
adminRuntimeHandler *handler.AdminRuntimeHandler,
|
2026-06-18 18:07:27 +08:00
|
|
|
openaiChatHandler *handler.OpenAIChatHandler,
|
feat[Go]: port agent attachment download, chatbot + agentbot completion/info endpoints from Python (#16405)
## Summary
Ports five Python agent APIs to Go under the v1 Gin router:
- `GET /api/v1/agents/attachments/<attachment_id>/download`
- `POST /api/v1/chatbots/<dialog_id>/completions` (SSE)
- `GET /api/v1/chatbots/<dialog_id>/info`
- `POST /api/v1/agentbots/<agent_id>/completions` (SSE)
- `GET /api/v1/agentbots/<agent_id>/inputs`
Mirrors the existing Python wire shape (`{code, message,
data:{answer,reference,...}}` per Python `canvas_service.completion`) so
the iframe SDK and existing JS widgets keep working.
## Behavioural parity with Python
| # | Concern | How it's met |
|---|---------|--------------|
| R0 | Bot routes must not require regular user session | Routes mount
on `apiNoAuth` (router.go:198-202), with `BetaAuthMiddleware` only |
| R3 | Two SSE formats in Go drift | F2: `AgentChatCompletions` and
`AgentbotCompletion` share `service.WriteChatbotRunEvent` |
| R7 | `GetBySessionID` returns `(nil, nil)` on miss | Defensive
nil-check before `session.UserID != tenantID` |
| R8 | Begin component name vs ID | `FindBeginComponentID` resolves name
→ ID first, then `ExtractComponentInputForm(dsl, beginID)` |
| R9 | Defensive PromptConfig parsing | `stringFromMap` helper used for
`prologue` and `tavily_api_key` |
| R10 | `BetaAuthMiddleware` Bearer-prefix pre-filter | Removed —
`GetUserByToken` is called unconditionally, falls back to
`GetUserByBetaAPIToken` |
| F8 | Multi-turn chatbot history | `ChatbotCompletion` reads prior
turns from `session.Message`, appends user turn, calls LLM, persists new
pair via new `API4ConversationDAO.Update` |
| F9 | UUID gate stricter than plan | Removed — only `filepath.Base` +
CR/LF/quote header sanitization remains |
| H2 | Defence-in-depth IDOR | `AgentbotCompletion` calls `loadCanvas`
before delegating to `RunAgent` |
| M2 | SSE error leakage | `WriteChatbotFrame` emits generic `"an
internal error occurred"`; real error logged via `common.Error` |
## Verification
```bash
$ go vet ./... # clean (only pre-existing issues)
$ go build ./... # success
$ go test ./internal/handler/ ./internal/service/ ./internal/agent/dsl/ ./internal/common/ ./internal/dao/
ok ragflow/internal/handler 0.617s
ok ragflow/internal/service 1.729s
ok ragflow/internal/agent/dsl 0.008s
ok ragflow/internal/common 0.087s
ok ragflow/internal/dao 0.083s
```
1199 tests pass across 5 packages.
## Known follow-ups (out of scope for this PR)
- **F1**: token-level streaming in `ChatbotCompletion` (currently emits
one frame per turn)
- **F3**: per-route `auth_types` attribute in Go (currently applied via
route group middleware)
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-06-27 16:52:21 +08:00
|
|
|
botHandler *handler.BotHandler,
|
2026-07-05 20:43:52 +08:00
|
|
|
componentsHandler *handler.ComponentsHandler,
|
2026-03-04 19:17:16 +08:00
|
|
|
) *Router {
|
|
|
|
|
return &Router{
|
2026-06-08 21:38:15 +08:00
|
|
|
authHandler: authHandler,
|
|
|
|
|
userHandler: userHandler,
|
|
|
|
|
tenantHandler: tenantHandler,
|
|
|
|
|
documentHandler: documentHandler,
|
|
|
|
|
datasetsHandler: datasetsHandler,
|
|
|
|
|
systemHandler: systemHandler,
|
|
|
|
|
chunkHandler: chunkHandler,
|
|
|
|
|
llmHandler: llmHandler,
|
|
|
|
|
chatHandler: chatHandler,
|
2026-06-22 18:16:15 +08:00
|
|
|
chatChannelHandler: chatChannelHandler,
|
2026-06-25 19:25:55 +08:00
|
|
|
langfuseHandler: langfuseHandler,
|
2026-06-18 18:07:27 +08:00
|
|
|
openaiChatHandler: openaiChatHandler,
|
2026-06-08 21:38:15 +08:00
|
|
|
chatSessionHandler: chatSessionHandler,
|
|
|
|
|
connectorHandler: connectorHandler,
|
|
|
|
|
searchHandler: searchHandler,
|
|
|
|
|
fileHandler: fileHandler,
|
|
|
|
|
memoryHandler: memoryHandler,
|
|
|
|
|
mcpHandler: mcpHandler,
|
2026-07-02 09:45:01 +08:00
|
|
|
mcpServerHandler: mcpServerHandler,
|
2026-06-08 21:38:15 +08:00
|
|
|
skillSearchHandler: skillSearchHandler,
|
|
|
|
|
providerHandler: providerHandler,
|
|
|
|
|
agentHandler: agentHandler,
|
|
|
|
|
searchBotHandler: searchBotHandler,
|
|
|
|
|
difyRetrievalHandler: difyRetrievalHandler,
|
|
|
|
|
pluginHandler: pluginHandler,
|
|
|
|
|
modelHandler: modelHandler,
|
2026-06-15 11:19:56 +08:00
|
|
|
fileCommitHandler: fileCommitHandler,
|
feat(agent): Go port — canvas engine, 22 components, DSL v2, 13 endpoints (#15952)
Ports the agent canvas subsystem from Python to Go.
## What's included
### Canvas Engine (Phase 0/1)
- State engine, scheduler, variable resolver, Redis checkpoint store,
cancel protocol
- **209 tests** across canvas / component / io packages
### 22 Components (P0–P4)
| Tier | Components |
|---|---|
| P0 T1+T2+T3 | LLM, Agent, ExitLoop, Switch, Categorize, Begin,
Message, Invoke |
| P1 T3 | VariableAggregator, VariableAssigner, StringTransform,
ListOperations, DataOperations |
| P2 T3 | Iteration, IterationItem, Loop, LoopItem |
| P3 T3 | UserFillUp, Fillup |
| P4 T5 | Browser, ExcelProcessor, DocsGenerator |
### DSL v2 Schema (Phase 2.5)
- Typed v2 in-memory model with v1-to-v2 auto-detect converter
- v1 legacy field stripping per plan §2.11.7
### HTTP Endpoints & Bug Fixes (Plans PR1–PR3)
- **DELETE SQL bug fix**: gorm v2 `Where("id = ?", id).Delete(...)`
pattern
- **CreateAgent validation**: title/DSL required, duplicate check, 103
envelope
- **13 new endpoints**: templates, prompts, tags, sessions CRUD,
chat/completions (SSE + non-stream stubs), rerun, test_db_connection,
logs, webhook/logs
- **756 Go unit tests** (745 → 756, +18)
- **17 → 0 Python integration test failures** (test_agents.py +
test_session_management/)
### Tools
21 eino tools: HTTPHelper, search tools, financial/data tools, mandatory
stubs
### Infrastructure
OTel observability, NATS message queue, DeepDoc gRPC client, SSRF
guards, IDOR mitigation
2026-06-12 22:58:28 +08:00
|
|
|
adminRuntimeHandler: adminRuntimeHandler,
|
feat[Go]: port agent attachment download, chatbot + agentbot completion/info endpoints from Python (#16405)
## Summary
Ports five Python agent APIs to Go under the v1 Gin router:
- `GET /api/v1/agents/attachments/<attachment_id>/download`
- `POST /api/v1/chatbots/<dialog_id>/completions` (SSE)
- `GET /api/v1/chatbots/<dialog_id>/info`
- `POST /api/v1/agentbots/<agent_id>/completions` (SSE)
- `GET /api/v1/agentbots/<agent_id>/inputs`
Mirrors the existing Python wire shape (`{code, message,
data:{answer,reference,...}}` per Python `canvas_service.completion`) so
the iframe SDK and existing JS widgets keep working.
## Behavioural parity with Python
| # | Concern | How it's met |
|---|---------|--------------|
| R0 | Bot routes must not require regular user session | Routes mount
on `apiNoAuth` (router.go:198-202), with `BetaAuthMiddleware` only |
| R3 | Two SSE formats in Go drift | F2: `AgentChatCompletions` and
`AgentbotCompletion` share `service.WriteChatbotRunEvent` |
| R7 | `GetBySessionID` returns `(nil, nil)` on miss | Defensive
nil-check before `session.UserID != tenantID` |
| R8 | Begin component name vs ID | `FindBeginComponentID` resolves name
→ ID first, then `ExtractComponentInputForm(dsl, beginID)` |
| R9 | Defensive PromptConfig parsing | `stringFromMap` helper used for
`prologue` and `tavily_api_key` |
| R10 | `BetaAuthMiddleware` Bearer-prefix pre-filter | Removed —
`GetUserByToken` is called unconditionally, falls back to
`GetUserByBetaAPIToken` |
| F8 | Multi-turn chatbot history | `ChatbotCompletion` reads prior
turns from `session.Message`, appends user turn, calls LLM, persists new
pair via new `API4ConversationDAO.Update` |
| F9 | UUID gate stricter than plan | Removed — only `filepath.Base` +
CR/LF/quote header sanitization remains |
| H2 | Defence-in-depth IDOR | `AgentbotCompletion` calls `loadCanvas`
before delegating to `RunAgent` |
| M2 | SSE error leakage | `WriteChatbotFrame` emits generic `"an
internal error occurred"`; real error logged via `common.Error` |
## Verification
```bash
$ go vet ./... # clean (only pre-existing issues)
$ go build ./... # success
$ go test ./internal/handler/ ./internal/service/ ./internal/agent/dsl/ ./internal/common/ ./internal/dao/
ok ragflow/internal/handler 0.617s
ok ragflow/internal/service 1.729s
ok ragflow/internal/agent/dsl 0.008s
ok ragflow/internal/common 0.087s
ok ragflow/internal/dao 0.083s
```
1199 tests pass across 5 packages.
## Known follow-ups (out of scope for this PR)
- **F1**: token-level streaming in `ChatbotCompletion` (currently emits
one frame per turn)
- **F3**: per-route `auth_types` attribute in Go (currently applied via
route group middleware)
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-06-27 16:52:21 +08:00
|
|
|
botHandler: botHandler,
|
2026-07-05 20:43:52 +08:00
|
|
|
componentsHandler: componentsHandler,
|
2026-03-04 19:17:16 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Setup setup routes
|
|
|
|
|
func (r *Router) Setup(engine *gin.Engine) {
|
2026-06-04 15:36:26 +08:00
|
|
|
// Mark all responses from Go with a header for debugging.
|
|
|
|
|
engine.Use(func(c *gin.Context) {
|
|
|
|
|
c.Header("X-API-Source", "go")
|
|
|
|
|
c.Next()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Log all HTTP requests.
|
2026-06-23 16:21:46 +08:00
|
|
|
engine.Use(common.GinLogger())
|
2026-06-04 15:36:26 +08:00
|
|
|
|
2026-03-04 19:17:16 +08:00
|
|
|
// Health check
|
2026-03-30 18:40:58 +08:00
|
|
|
engine.GET("/health", r.systemHandler.Health)
|
2026-03-04 19:17:16 +08:00
|
|
|
|
|
|
|
|
// System endpoints
|
|
|
|
|
engine.GET("/v1/system/configs", r.systemHandler.GetConfigs)
|
2026-05-08 15:53:06 +08:00
|
|
|
//engine.POST("/v1/user/register", r.userHandler.Register)
|
2026-03-11 11:23:13 +08:00
|
|
|
|
2026-03-12 20:02:50 +08:00
|
|
|
// User logout endpoint
|
|
|
|
|
engine.GET("/v1/user/logout", r.userHandler.Logout)
|
|
|
|
|
|
2026-06-03 20:08:55 +08:00
|
|
|
// OAuth callbacks are invoked by third-party providers and cannot rely on
|
|
|
|
|
// the RAGFlow auth middleware.
|
|
|
|
|
engine.GET("/connectors/gmail/oauth/web/callback", r.connectorHandler.GmailWebOAuthCallback)
|
|
|
|
|
engine.GET("/connectors/google-drive/oauth/web/callback", r.connectorHandler.GoogleDriveWebOAuthCallback)
|
2026-06-30 18:10:36 +08:00
|
|
|
engine.GET("/connectors/box/oauth/web/callback", r.connectorHandler.BoxWebOAuthCallback)
|
2026-06-03 20:08:55 +08:00
|
|
|
|
2026-05-08 13:56:19 +08:00
|
|
|
apiNoAuth := engine.Group("/api/v1")
|
|
|
|
|
{
|
|
|
|
|
apiNoAuth.GET("/system/ping", r.systemHandler.Ping)
|
|
|
|
|
apiNoAuth.GET("/system/config", r.systemHandler.GetConfig)
|
|
|
|
|
apiNoAuth.GET("/system/version", r.systemHandler.GetVersion)
|
Go: implement system healthz API (#15307)
## Summary
- Add Go REST support for `GET /api/v1/system/healthz`.
- Return Python-compatible `ok`/`nok` dependency fields for DB, Redis,
document engine, and storage.
- Return HTTP 200 only when all checks pass; otherwise return HTTP 500
with `_meta` failure details.
- Add focused service coverage for the unhealthy dependency response
when Go dependencies are not initialized.
## Scope
This is a small, isolated slice of #15240. It avoids current open
connector PRs (#15274, #15300, #15265, #15264), tenant/member PRs
(#15295, #15301, #15276), MCP PRs (#15281, #15253, #15254, #15260,
#15261, #15262), and the memory-message PR (#15256).
Refs #15240
2026-05-27 19:30:22 -10:00
|
|
|
apiNoAuth.GET("/system/healthz", r.systemHandler.Healthz)
|
2026-05-08 13:56:19 +08:00
|
|
|
|
2026-06-22 18:17:37 +08:00
|
|
|
// searchbots
|
|
|
|
|
apiNoAuth.GET("/searchbots/detail", r.searchBotHandler.SearchbotDetail)
|
|
|
|
|
|
2026-05-08 13:56:19 +08:00
|
|
|
// User login channels endpoint
|
|
|
|
|
apiNoAuth.GET("/auth/login/channels", r.userHandler.GetLoginChannels)
|
|
|
|
|
|
|
|
|
|
// User login by email endpoint
|
|
|
|
|
apiNoAuth.POST("/auth/login", r.userHandler.LoginByEmail)
|
|
|
|
|
|
2026-06-01 19:38:02 -06:00
|
|
|
// OAuth / OIDC login routes. The static "channels" segment is
|
|
|
|
|
// registered before the wildcard, so gin's tree resolves
|
|
|
|
|
// /auth/login/channels to GetLoginChannels and other values to
|
|
|
|
|
// OAuthLogin without conflict.
|
|
|
|
|
apiNoAuth.GET("/auth/login/:channel", r.userHandler.OAuthLogin)
|
|
|
|
|
apiNoAuth.GET("/auth/oauth/:channel/callback", r.userHandler.OAuthCallback)
|
|
|
|
|
|
2026-05-08 13:56:19 +08:00
|
|
|
// Register
|
|
|
|
|
apiNoAuth.POST("/users", r.userHandler.Register)
|
2026-06-01 11:22:08 +08:00
|
|
|
|
2026-06-03 20:08:55 +08:00
|
|
|
// Google redirects here after Gmail / Google Drive web OAuth completes.
|
|
|
|
|
apiNoAuth.GET("/connectors/gmail/oauth/web/callback", r.connectorHandler.GmailWebOAuthCallback)
|
|
|
|
|
apiNoAuth.GET("/connectors/google-drive/oauth/web/callback", r.connectorHandler.GoogleDriveWebOAuthCallback)
|
2026-06-30 18:10:36 +08:00
|
|
|
apiNoAuth.GET("/connectors/box/oauth/web/callback", r.connectorHandler.BoxWebOAuthCallback)
|
feat(go-api): port forgot-password flow to Go (#15282) (#15290)
## Summary
Implements **chunk 1** of #15282 — the four `/api/v1/auth/password/...`
endpoints from the login-page Go port. **Chunk 2 (OAuth/OIDC) is
deferred** to its own subtask, matching the issue author's own
confidence-low recommendation ("multi-provider, stateful redirect flow
with external dependencies; recommend its own subtask").
New endpoints, all registered under `apiNoAuth` (forgot-password users
are unauthenticated by definition):
| Method | Path | Status |
|--------|------|--------|
| `POST` | `/api/v1/auth/password/forgot/captcha` | new |
| `POST` | `/api/v1/auth/password/forgot/otp` | new |
| `POST` | `/api/v1/auth/password/forgot/otp/verify` | new |
| `POST` | `/api/v1/auth/password/reset` | new |
## Wire compatibility with the Python backend
The two backends share state through Redis, so the Go port had to use
identical keys, encodings, and constants. Either backend can now
validate a code the other minted.
- **Redis keys**: `captcha:<email>`, `otp:<email>`,
`otp_attempts:<email>`, `otp_last_sent:<email>`, `otp_lock:<email>`,
`otp:verified:<email>` — same as `api/utils/web_utils.py`.
- **Stored OTP value**: `"<hex_hash>:<hex_salt>"` — same as Python.
- **Hash**: HMAC-SHA256 with a `crypto/rand` 16-byte salt — same as
`hash_code()`.
- **Constants**: `OTP_LENGTH=4`, `OTP_TTL=5min`, `ATTEMPT_LIMIT=5`,
`ATTEMPT_LOCK_SECONDS=30min`, `RESEND_COOLDOWN_SECONDS=60s` — all match
`api/utils/web_utils.py`.
- **Email body**: matches `RESET_CODE_EMAIL_TMPL` byte-for-byte.
## Files
### New
| File | Purpose |
|---|---|
| `internal/utility/otp.go` | OTP/captcha constants, Redis key builders
(`CaptchaRedisKey`, `OTPRedisKeys`, `OTPVerifiedRedisKey`),
`HashOTPCode`, `GenerateOTPCode` / `GenerateCaptchaCode` /
`GenerateOTPSalt` via `crypto/rand`, and `EncodeOTPStorageValue` /
`DecodeOTPStorageValue` matching Python's storage shape. |
| `internal/utility/smtp.go` | Minimal stdlib `net/smtp` sender.
`SendResetCodeEmail(to, otp, ttlMin)` builds an RFC 5322 plain-text
message and dispatches via implicit TLS / STARTTLS / plain — same
selectors as Python `aiosmtplib`. Returns `SMTPNotConfiguredError` if
the config block is empty. |
### Modified
| File | Change |
|---|---|
| `internal/server/config.go` | New `SMTPConfig` struct + `Config.SMTP`
field. Field names mirror the `smtp:` keys in `common/settings.py`
(`mail_server`, `mail_port`, `mail_use_ssl`, `mail_use_tls`,
`mail_username`, `mail_password`, `mail_from_name`, `mail_from_address`,
`mail_frontend_url`) so a single `conf/service_conf.yaml` powers both
backends. |
| `internal/service/user.go` | Four methods — `ForgotIssueCaptcha`,
`ForgotSendOTP`, `ForgotVerifyOTP`, `ForgotResetPassword`. Reuses the
existing `decryptPassword`, `HashPassword`, `userDAO.Update`, and
`utility.GenerateToken` so the reset+auto-login path is identical to
`LoginByEmail`. |
| `internal/handler/user.go` | Four handlers in the same `c.JSON` shape
as `LoginByEmail`. The reset handler rotates the access token and emits
an `Authorization` header for auto-login (matches Python
`construct_response(auth=user.get_id())`). |
| `internal/router/router.go` | Routes registered under `apiNoAuth`,
with an explanatory comment on why they sit outside the auth middleware.
|
## Known divergence — captcha rendering
The Python endpoint returns a rendered `image/JPEG` from the
`python-captcha` library. The Go side has **no image-captcha dependency
vendored** in `go.mod`, and hand-rolling a raster generator was out of
scope for this PR.
This commit returns JSON `{captcha: "<text>"}` instead. Implications:
- **Backend gate is identical** — the OTP step still verifies the
user-submitted captcha string against the Redis value, so the security
model is unchanged.
- **Frontend impact**: the password-reset page rendering needs a small
tweak (text display instead of `<img>`) until a Go captcha library is
wired in.
- The handler comments call this out explicitly so the next PR knows
what to swap.
Possible follow-ups (any one closes the gap):
1. Add `github.com/mojocn/base64Captcha` or `github.com/dchest/captcha`
to `go.mod` and replace the JSON response with an `image/JPEG`.
2. Hand-roll a 5x7 bitmap font + `image/png` writer using only the
stdlib.
3. Render a server-side SVG (cheap, but trivially OCR-able — only useful
as a UI shim).
## Test plan
- [ ] **Captcha**: `POST
/api/v1/auth/password/forgot/captcha?email=<existing>` returns `{code:
0, data: {captcha: "ABCD"}}`. Redis shows `captcha:<email>` with that
value and ~60s TTL. Unknown email returns `code: CodeDataError`.
- [ ] **OTP send**: `POST /api/v1/auth/password/forgot/otp` with the
right captcha mints an OTP, stores `<hash>:<salt>` under `otp:<email>`
for 5 min, sends an email, returns success. With a wrong captcha returns
`CodeAuthenticationError`. Hitting it again within 60s returns "you
still have to wait …" with `CodeNotEffective`.
- [ ] **OTP verify**: correct OTP → `code: 0`, OTP keys cleared,
`otp:verified:<email>` = `"1"`. Wrong OTP → `code:
CodeAuthenticationError`, attempt counter bumped; after 5 wrong tries
`otp_lock:<email>` is set and further attempts hit `CodeNotEffective`.
- [ ] **Reset**: with the verified flag set, supply a new password
(RSA-encrypted+base64, same as `LoginByEmail`). Returns `code: 0`,
`Authorization` header set, verified flag deleted. Without the verified
flag returns `CodeAuthenticationError`.
- [ ] **Wire-compat smoke**: mint an OTP from the Python backend, verify
it via the Go endpoint, and vice versa. Should both succeed.
- [ ] **SMTP misconfigured**: drop `smtp.mail_server` from
`conf/service_conf.yaml`. The OTP-send endpoint should now return
"failed to send email" without panicking; check the log for the
`SMTPNotConfiguredError` warning.
- [ ] **End-to-end FE**: hit the password-reset flow from
`web/src/pages/login-next/`. Confirm the text-captcha shim works after
the FE tweak.
- [ ] `go build ./...` and `go vet ./...` — I could not run these in the
sandbox; please confirm a clean build before merging.
- [ ] `uv run pytest` to confirm no Python regressions (shared Redis
schema).
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
2026-06-10 06:27:56 -07:00
|
|
|
// Forgot-password flow (fixes #15282).
|
|
|
|
|
// Routes are intentionally registered before any auth middleware:
|
|
|
|
|
// a user who has forgotten their password is, by definition,
|
|
|
|
|
// unauthenticated.
|
|
|
|
|
apiNoAuth.POST("/auth/password/forgot/captcha", r.userHandler.ForgotCaptcha)
|
|
|
|
|
apiNoAuth.POST("/auth/password/forgot/otp", r.userHandler.ForgotSendOTP)
|
|
|
|
|
apiNoAuth.POST("/auth/password/forgot/otp/verify", r.userHandler.ForgotVerifyOTP)
|
|
|
|
|
apiNoAuth.POST("/auth/password/reset", r.userHandler.ForgotResetPassword)
|
2026-07-03 17:00:43 +08:00
|
|
|
|
|
|
|
|
apiNoAuth.GET("/dify/retrieval/health", r.difyRetrievalHandler.HealthCheck)
|
2026-06-29 16:44:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Beta-token routes. Mirrors python's
|
|
|
|
|
// @login_required(auth_types=AUTH_BETA) on bot_api.py bot endpoints.
|
|
|
|
|
apiBetaAuth := engine.Group("/api/v1")
|
|
|
|
|
apiBetaAuth.Use(r.authHandler.BetaAuthMiddleware())
|
|
|
|
|
{
|
|
|
|
|
searchbotGroup := apiBetaAuth.Group("/searchbots")
|
|
|
|
|
searchbotGroup.POST("/related_questions", r.searchBotHandler.Handle)
|
|
|
|
|
searchbotGroup.POST("/retrieval_test", r.searchBotHandler.RetrievalTest)
|
|
|
|
|
searchbotGroup.POST("/ask", r.searchBotHandler.Ask)
|
2026-06-30 16:35:33 +08:00
|
|
|
searchbotGroup.POST("/mindmap", r.searchBotHandler.MindMap)
|
feat[Go]: port agent attachment download, chatbot + agentbot completion/info endpoints from Python (#16405)
## Summary
Ports five Python agent APIs to Go under the v1 Gin router:
- `GET /api/v1/agents/attachments/<attachment_id>/download`
- `POST /api/v1/chatbots/<dialog_id>/completions` (SSE)
- `GET /api/v1/chatbots/<dialog_id>/info`
- `POST /api/v1/agentbots/<agent_id>/completions` (SSE)
- `GET /api/v1/agentbots/<agent_id>/inputs`
Mirrors the existing Python wire shape (`{code, message,
data:{answer,reference,...}}` per Python `canvas_service.completion`) so
the iframe SDK and existing JS widgets keep working.
## Behavioural parity with Python
| # | Concern | How it's met |
|---|---------|--------------|
| R0 | Bot routes must not require regular user session | Routes mount
on `apiNoAuth` (router.go:198-202), with `BetaAuthMiddleware` only |
| R3 | Two SSE formats in Go drift | F2: `AgentChatCompletions` and
`AgentbotCompletion` share `service.WriteChatbotRunEvent` |
| R7 | `GetBySessionID` returns `(nil, nil)` on miss | Defensive
nil-check before `session.UserID != tenantID` |
| R8 | Begin component name vs ID | `FindBeginComponentID` resolves name
→ ID first, then `ExtractComponentInputForm(dsl, beginID)` |
| R9 | Defensive PromptConfig parsing | `stringFromMap` helper used for
`prologue` and `tavily_api_key` |
| R10 | `BetaAuthMiddleware` Bearer-prefix pre-filter | Removed —
`GetUserByToken` is called unconditionally, falls back to
`GetUserByBetaAPIToken` |
| F8 | Multi-turn chatbot history | `ChatbotCompletion` reads prior
turns from `session.Message`, appends user turn, calls LLM, persists new
pair via new `API4ConversationDAO.Update` |
| F9 | UUID gate stricter than plan | Removed — only `filepath.Base` +
CR/LF/quote header sanitization remains |
| H2 | Defence-in-depth IDOR | `AgentbotCompletion` calls `loadCanvas`
before delegating to `RunAgent` |
| M2 | SSE error leakage | `WriteChatbotFrame` emits generic `"an
internal error occurred"`; real error logged via `common.Error` |
## Verification
```bash
$ go vet ./... # clean (only pre-existing issues)
$ go build ./... # success
$ go test ./internal/handler/ ./internal/service/ ./internal/agent/dsl/ ./internal/common/ ./internal/dao/
ok ragflow/internal/handler 0.617s
ok ragflow/internal/service 1.729s
ok ragflow/internal/agent/dsl 0.008s
ok ragflow/internal/common 0.087s
ok ragflow/internal/dao 0.083s
```
1199 tests pass across 5 packages.
## Known follow-ups (out of scope for this PR)
- **F1**: token-level streaming in `ChatbotCompletion` (currently emits
one frame per turn)
- **F3**: per-route `auth_types` attribute in Go (currently applied via
route group middleware)
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-06-27 16:52:21 +08:00
|
|
|
|
|
|
|
|
if r.botHandler != nil {
|
2026-06-29 16:44:21 +08:00
|
|
|
chatbotGroup := apiBetaAuth.Group("/chatbots")
|
2026-06-30 16:28:48 +08:00
|
|
|
betaMW := r.authHandler.BetaAuthMiddleware()
|
|
|
|
|
RegisterChatbotRoutes(chatbotGroup, betaMW, r.botHandler)
|
2026-06-29 16:44:21 +08:00
|
|
|
agentbotGroup := apiBetaAuth.Group("/agentbots")
|
2026-06-30 16:28:48 +08:00
|
|
|
RegisterAgentbotRoutes(agentbotGroup, betaMW, r.botHandler)
|
feat[Go]: port agent attachment download, chatbot + agentbot completion/info endpoints from Python (#16405)
## Summary
Ports five Python agent APIs to Go under the v1 Gin router:
- `GET /api/v1/agents/attachments/<attachment_id>/download`
- `POST /api/v1/chatbots/<dialog_id>/completions` (SSE)
- `GET /api/v1/chatbots/<dialog_id>/info`
- `POST /api/v1/agentbots/<agent_id>/completions` (SSE)
- `GET /api/v1/agentbots/<agent_id>/inputs`
Mirrors the existing Python wire shape (`{code, message,
data:{answer,reference,...}}` per Python `canvas_service.completion`) so
the iframe SDK and existing JS widgets keep working.
## Behavioural parity with Python
| # | Concern | How it's met |
|---|---------|--------------|
| R0 | Bot routes must not require regular user session | Routes mount
on `apiNoAuth` (router.go:198-202), with `BetaAuthMiddleware` only |
| R3 | Two SSE formats in Go drift | F2: `AgentChatCompletions` and
`AgentbotCompletion` share `service.WriteChatbotRunEvent` |
| R7 | `GetBySessionID` returns `(nil, nil)` on miss | Defensive
nil-check before `session.UserID != tenantID` |
| R8 | Begin component name vs ID | `FindBeginComponentID` resolves name
→ ID first, then `ExtractComponentInputForm(dsl, beginID)` |
| R9 | Defensive PromptConfig parsing | `stringFromMap` helper used for
`prologue` and `tavily_api_key` |
| R10 | `BetaAuthMiddleware` Bearer-prefix pre-filter | Removed —
`GetUserByToken` is called unconditionally, falls back to
`GetUserByBetaAPIToken` |
| F8 | Multi-turn chatbot history | `ChatbotCompletion` reads prior
turns from `session.Message`, appends user turn, calls LLM, persists new
pair via new `API4ConversationDAO.Update` |
| F9 | UUID gate stricter than plan | Removed — only `filepath.Base` +
CR/LF/quote header sanitization remains |
| H2 | Defence-in-depth IDOR | `AgentbotCompletion` calls `loadCanvas`
before delegating to `RunAgent` |
| M2 | SSE error leakage | `WriteChatbotFrame` emits generic `"an
internal error occurred"`; real error logged via `common.Error` |
## Verification
```bash
$ go vet ./... # clean (only pre-existing issues)
$ go build ./... # success
$ go test ./internal/handler/ ./internal/service/ ./internal/agent/dsl/ ./internal/common/ ./internal/dao/
ok ragflow/internal/handler 0.617s
ok ragflow/internal/service 1.729s
ok ragflow/internal/agent/dsl 0.008s
ok ragflow/internal/common 0.087s
ok ragflow/internal/dao 0.083s
```
1199 tests pass across 5 packages.
## Known follow-ups (out of scope for this PR)
- **F1**: token-level streaming in `ChatbotCompletion` (currently emits
one frame per turn)
- **F3**: per-route `auth_types` attribute in Go (currently applied via
route group middleware)
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-06-27 16:52:21 +08:00
|
|
|
}
|
feat(go): implement chatbots/<dialog_id>/info and searchbots/detail (#15420)
### What problem does this PR solve?
Part of #15240 (rewriting the RAGFlow API server in Go).
Implements the two public bot endpoints from
`api/apps/restful_apis/bot_api.py`:
- **`GET /api/v1/chatbots/<dialog_id>/info`** (`chatbots_inputs`) —
returns `{title, avatar, prologue, has_tavily_key}` for a dialog the
authenticated tenant owns (tenant match + `status == VALID`), otherwise
`"Authentication error: no access to this chatbot!"`.
- **`GET /api/v1/searchbots/detail`** (`detail_share_embedded`) —
returns search-app detail for a `search_id` the tenant can access.
Permission is checked across the tenant's joined tenants; denial returns
`"Has no permission for this operation."` (operating error, `data:
false`) and a missing app returns `"Can't find this Search App!"`.
Both endpoints authenticate with an SDK **beta token** (`Authorization:
Bearer <beta>`) rather than a session — the token is resolved to a
tenant via `APIToken.query(beta=token)`, backed by a new
`APITokenDAO.GetByBeta`. Because they perform their own token-based
auth, the routes are registered on the unauthenticated route group
(mirroring the Python blueprint, which has no `@login_required`).
Both live in a new `internal/handler/bot.go` + `internal/service/bot.go`
since they share the same source module. Handler unit tests cover the
auth, success, and error-mapping paths.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Claude Code <claude@anthropic.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Ling Qin <qinling0210@163.com>
2026-07-02 00:46:00 -10:00
|
|
|
// Public bot endpoints (authenticated with an SDK beta token, not a session)
|
2026-06-29 19:08:49 +08:00
|
|
|
apiBetaAuth.GET("/documents/:id/preview", r.documentHandler.GetDocumentPreview)
|
2026-07-02 16:05:49 +08:00
|
|
|
apiBetaAuth.GET("/documents/images/:image_id", r.documentHandler.GetDocumentImage)
|
2026-06-29 19:08:49 +08:00
|
|
|
apiBetaAuth.GET("/thumbnails", r.documentHandler.GetThumbnail)
|
2026-07-02 09:45:01 +08:00
|
|
|
|
|
|
|
|
// MCP server endpoint — exposes RAGFlow capabilities as MCP tools.
|
|
|
|
|
// Uses BetaAuthMiddleware to resolve the user from the
|
|
|
|
|
// Authorization header.
|
|
|
|
|
if r.mcpServerHandler != nil {
|
|
|
|
|
apiBetaAuth.POST("/mcp", r.mcpServerHandler.HandleMCP)
|
|
|
|
|
}
|
2026-05-08 13:56:19 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-11 11:23:13 +08:00
|
|
|
// Protected routes
|
|
|
|
|
authorized := engine.Group("")
|
|
|
|
|
authorized.Use(r.authHandler.AuthMiddleware())
|
2026-03-04 19:17:16 +08:00
|
|
|
{
|
2026-03-11 11:23:13 +08:00
|
|
|
// User info endpoint
|
|
|
|
|
authorized.GET("/v1/user/info", r.userHandler.Info)
|
|
|
|
|
// User tenant info endpoint
|
|
|
|
|
authorized.GET("/v1/user/tenant_info", r.tenantHandler.TenantInfo)
|
|
|
|
|
// Tenant list endpoint
|
|
|
|
|
authorized.GET("/v1/tenant/list", r.tenantHandler.TenantList)
|
|
|
|
|
// User settings endpoint
|
|
|
|
|
authorized.POST("/v1/user/setting", r.userHandler.Setting)
|
|
|
|
|
// User change password endpoint
|
|
|
|
|
authorized.POST("/v1/user/setting/password", r.userHandler.ChangePassword)
|
|
|
|
|
// User set tenant info endpoint
|
|
|
|
|
authorized.POST("/v1/user/set_tenant_info", r.userHandler.SetTenantInfo)
|
|
|
|
|
|
|
|
|
|
// API v1 route group
|
|
|
|
|
v1 := authorized.Group("/api/v1")
|
2026-03-04 19:17:16 +08:00
|
|
|
{
|
2026-05-07 17:14:22 +08:00
|
|
|
// Auth routes
|
|
|
|
|
auth := v1.Group("/auth")
|
|
|
|
|
{
|
|
|
|
|
// User logout endpoint
|
2026-05-08 15:53:06 +08:00
|
|
|
auth.POST("/logout", r.userHandler.Logout)
|
2026-05-07 17:14:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Users routes
|
|
|
|
|
users := v1.Group("/users")
|
|
|
|
|
{
|
|
|
|
|
users.GET("/me", r.userHandler.Info)
|
|
|
|
|
// User settings endpoint
|
|
|
|
|
users.PATCH("/me", r.userHandler.Setting)
|
2026-05-18 16:57:14 +08:00
|
|
|
// User tenant info endpoint
|
|
|
|
|
users.GET("/me/models", r.tenantHandler.TenantInfo)
|
|
|
|
|
// User set tenant info endpoint
|
|
|
|
|
users.PATCH("/me/models", r.userHandler.SetTenantInfo)
|
2026-05-07 17:14:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tenants := v1.Group("/tenants")
|
|
|
|
|
{
|
|
|
|
|
tenants.GET("", r.tenantHandler.TenantList)
|
2026-05-28 20:13:09 -06:00
|
|
|
tenants.PATCH("/:tenant_id", r.tenantHandler.AcceptTenantInvite)
|
|
|
|
|
tenants.GET("/:tenant_id/users", r.tenantHandler.ListTenantMembers)
|
|
|
|
|
tenants.POST("/:tenant_id/users", r.tenantHandler.AddTenantMember)
|
|
|
|
|
tenants.DELETE("/:tenant_id/users", r.tenantHandler.RemoveTenantMember)
|
2026-05-07 17:14:22 +08:00
|
|
|
}
|
2026-03-11 11:23:13 +08:00
|
|
|
|
2026-07-03 17:00:43 +08:00
|
|
|
// Tenant routes (per-tenant resources)
|
|
|
|
|
tenant := v1.Group("/tenant")
|
|
|
|
|
{
|
|
|
|
|
tenant.GET("/list", r.tenantHandler.TenantList)
|
|
|
|
|
tenant.POST("/chunk_store", r.tenantHandler.CreateChunkStore) // Internal API only for GO
|
|
|
|
|
tenant.DELETE("/chunk_store", r.tenantHandler.DeleteChunkStore) // Internal API only for GO
|
|
|
|
|
tenant.POST("/metadata_store", r.tenantHandler.CreateMetadataStore) // Internal API only for GO
|
|
|
|
|
tenant.DELETE("/metadata_store", r.tenantHandler.DeleteMetadataStore) // Internal API only for GO
|
|
|
|
|
tenant.POST("/insert_chunks_from_file", r.tenantHandler.InsertChunksFromFile) // Internal API only for GO
|
|
|
|
|
tenant.POST("/insert_metadata_from_file", r.tenantHandler.InsertMetadataFromFile) // Internal API only for GO
|
|
|
|
|
}
|
2026-05-18 16:57:14 +08:00
|
|
|
|
2026-03-11 11:23:13 +08:00
|
|
|
// Document routes
|
|
|
|
|
documents := v1.Group("/documents")
|
|
|
|
|
{
|
|
|
|
|
documents.POST("", r.documentHandler.CreateDocument)
|
2026-06-24 14:52:47 +08:00
|
|
|
documents.POST("/upload", r.documentHandler.UploadInfo)
|
2026-03-11 11:23:13 +08:00
|
|
|
documents.GET("", r.documentHandler.ListDocuments)
|
2026-06-08 11:37:06 +08:00
|
|
|
documents.GET("/artifact/:filename", r.documentHandler.GetDocumentArtifact)
|
2026-03-11 11:23:13 +08:00
|
|
|
documents.GET("/:id", r.documentHandler.GetDocumentByID)
|
|
|
|
|
documents.PUT("/:id", r.documentHandler.UpdateDocument)
|
|
|
|
|
documents.DELETE("/:id", r.documentHandler.DeleteDocument)
|
2026-06-24 19:43:18 +08:00
|
|
|
documents.POST("/ingest", r.documentHandler.Ingest)
|
2026-03-11 11:23:13 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-07 17:14:22 +08:00
|
|
|
// Chat routes
|
|
|
|
|
chats := v1.Group("/chats")
|
|
|
|
|
{
|
|
|
|
|
chats.GET("", r.chatHandler.ListChats)
|
feat[Go]: implement Create-Chat/Session, Delete-Session (#16386)
### What problem does this PR solve?
As title:
implement:
```go
chats.POST("", r.chatHandler.Create)
chats.POST("/:chat_id/sessions", r.chatSessionHandler.CreateSession)
chats.DELETE("/:chat_id/sessions", r.chatSessionHandler.DeleteSessions)
```
bug fixed:
https://github.com/infiniflow/ragflow/blob/f80d4c784368853ec727231b6891860629973519/internal/handler/chat.go#L84
↓
```go
result, err := h.chatService.ListChats(userID, "1", keywords, page, pageSize, orderby, desc)
```
### Type of change
- [x] Bug Fix (non-breaking change which fixes an issue)
- [x] New Feature (non-breaking change which adds functionality)
- [x] Refactoring
2026-06-26 19:23:45 +08:00
|
|
|
chats.POST("", r.chatHandler.Create)
|
2026-06-22 18:16:52 +08:00
|
|
|
chats.DELETE("", r.chatHandler.BulkDeleteChats)
|
|
|
|
|
chats.DELETE("/:chat_id", r.chatHandler.DeleteChat)
|
2026-05-07 17:14:22 +08:00
|
|
|
chats.GET("/:chat_id", r.chatHandler.GetChat)
|
2026-06-26 19:22:57 +08:00
|
|
|
chats.PUT("/:chat_id", r.chatHandler.UpdateChat)
|
|
|
|
|
chats.PATCH("/:chat_id", r.chatHandler.PatchChat)
|
2026-05-07 17:14:22 +08:00
|
|
|
chats.GET("/:chat_id/sessions", r.chatSessionHandler.ListChatSessions)
|
feat[Go]: implement Create-Chat/Session, Delete-Session (#16386)
### What problem does this PR solve?
As title:
implement:
```go
chats.POST("", r.chatHandler.Create)
chats.POST("/:chat_id/sessions", r.chatSessionHandler.CreateSession)
chats.DELETE("/:chat_id/sessions", r.chatSessionHandler.DeleteSessions)
```
bug fixed:
https://github.com/infiniflow/ragflow/blob/f80d4c784368853ec727231b6891860629973519/internal/handler/chat.go#L84
↓
```go
result, err := h.chatService.ListChats(userID, "1", keywords, page, pageSize, orderby, desc)
```
### Type of change
- [x] Bug Fix (non-breaking change which fixes an issue)
- [x] New Feature (non-breaking change which adds functionality)
- [x] Refactoring
2026-06-26 19:23:45 +08:00
|
|
|
chats.POST("/:chat_id/sessions", r.chatSessionHandler.CreateSession)
|
|
|
|
|
chats.DELETE("/:chat_id/sessions", r.chatSessionHandler.DeleteSessions)
|
2026-06-24 17:34:01 +08:00
|
|
|
chats.GET("/:chat_id/sessions/:session_id", r.chatSessionHandler.GetSession)
|
|
|
|
|
chats.PATCH("/:chat_id/sessions/:session_id", r.chatSessionHandler.UpdateSession)
|
2026-07-02 10:33:27 +08:00
|
|
|
chats.DELETE("/:chat_id/sessions/:session_id/messages/:msg_id", r.chatSessionHandler.DeleteSessionMessage)
|
|
|
|
|
chats.PUT("/:chat_id/sessions/:session_id/messages/:msg_id/feedback", r.chatSessionHandler.UpdateMessageFeedback)
|
2026-05-07 17:14:22 +08:00
|
|
|
}
|
|
|
|
|
|
2026-07-01 15:52:52 +08:00
|
|
|
chat := v1.Group("/chat")
|
|
|
|
|
{
|
|
|
|
|
chat.POST("/completions", r.chatSessionHandler.ChatCompletions)
|
2026-07-03 17:00:43 +08:00
|
|
|
chat.POST("/mindmap", r.chatHandler.MindMap)
|
|
|
|
|
chat.POST("/recommendation", r.chatHandler.Recommendation)
|
2026-07-01 15:52:52 +08:00
|
|
|
}
|
2026-07-03 17:00:43 +08:00
|
|
|
v1.POST("/openai/:chat_id/chat/completions", r.openaiChatHandler.OpenAIChatCompletions)
|
2026-06-18 18:07:27 +08:00
|
|
|
|
2026-05-07 17:14:22 +08:00
|
|
|
// Dataset routes
|
2026-03-19 20:48:32 +08:00
|
|
|
datasets := v1.Group("/datasets")
|
|
|
|
|
{
|
|
|
|
|
datasets.GET("", r.datasetsHandler.ListDatasets)
|
2026-06-24 14:42:10 +08:00
|
|
|
datasets.GET("/tags/aggregation", r.datasetsHandler.AggregateTags)
|
2026-05-12 17:16:48 +08:00
|
|
|
datasets.GET("/:dataset_id", r.datasetsHandler.GetDataset)
|
2026-06-18 17:57:07 +08:00
|
|
|
datasets.PUT("/:dataset_id", r.datasetsHandler.UpdateDataset)
|
2026-05-18 20:02:53 +08:00
|
|
|
datasets.GET("/:dataset_id/graph", r.datasetsHandler.GetKnowledgeGraph)
|
2026-06-24 17:05:58 +08:00
|
|
|
datasets.GET("/:dataset_id/tags", r.datasetsHandler.ListTags)
|
|
|
|
|
datasets.PUT("/:dataset_id/tags", r.datasetsHandler.RenameTag)
|
2026-05-20 20:32:06 +08:00
|
|
|
datasets.DELETE("/:dataset_id/tags", r.datasetsHandler.RemoveTags)
|
2026-06-24 19:09:43 +08:00
|
|
|
datasets.POST("/:dataset_id/embedding", r.datasetsHandler.RunEmbedding)
|
|
|
|
|
datasets.POST("/:dataset_id/embedding/check", r.datasetsHandler.CheckEmbedding)
|
2026-06-23 19:19:08 +08:00
|
|
|
datasets.POST("/:dataset_id/documents/batch-update-status", r.documentHandler.BatchUpdateDocumentStatus)
|
feat[Go]: implement datasets/<dataset_id>/index P/G (#16153)
### What problem does this PR solve?
```
POST: http://localhost:9384/api/v1/datasets/433b390c630411f1a13eab5f89540b2a/index?type=graph
Output: {
"code": 0,
"data": {
"task_id": "ff5a3546bafa49d794a9a050d99c4a52"
},
"message": "success"
}
```
---
```
GET: http://localhost:9384/api/v1/datasets/433b390c630411f1a13eab5f89540b2a/index?type=graph
Output: {
"code": 0,
"data": {
"id": "ff5a3546bafa49d794a9a050d99c4a52",
"doc_id": "graph_raptor_x",
"from_page": 100000000,
"to_page": 100000000,
"task_type": "graphrag",
"priority": 0,
"begin_at": "2026-06-17T18:07:45+08:00",
"process_duration": 4.108135,
"progress": -1,
"progress_msg": "18:07:45 created task graphrag\n18:07:47 Task has been received.\n18:07:49 [ERROR][Exception]: Model config not found: Qwen/Qwen3-235B-A22B@test@SILICONFLOW",
"retry_count": 1,
"digest": "f16fd067d5c92cec",
"create_time": 1781690865552,
"create_date": "2026-06-17T18:07:45+08:00",
"update_time": 1781690869108,
"update_date": "2026-06-17T18:07:49+08:00"
},
"message": "success"
}
```
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
2026-06-18 17:57:24 +08:00
|
|
|
datasets.GET("/:dataset_id/index", r.datasetsHandler.TraceIndex)
|
|
|
|
|
datasets.POST("/:dataset_id/index", r.datasetsHandler.RunIndex)
|
2026-06-24 14:47:55 +08:00
|
|
|
datasets.DELETE("/:dataset_id/index", r.datasetsHandler.DeleteIndex)
|
|
|
|
|
datasets.DELETE("/:dataset_id/:index_type", r.datasetsHandler.DeleteIndex)
|
|
|
|
|
//datasets.DELETE("/:dataset_id/graph", r.datasetsHandler.DeleteKnowledgeGraph)
|
2026-03-19 20:48:32 +08:00
|
|
|
datasets.POST("", r.datasetsHandler.CreateDataset)
|
|
|
|
|
datasets.DELETE("", r.datasetsHandler.DeleteDatasets)
|
2026-06-08 11:49:37 +08:00
|
|
|
datasets.POST("/search", r.datasetsHandler.SearchDatasets)
|
2026-06-25 13:32:22 +08:00
|
|
|
datasets.POST("/:dataset_id/search", r.datasetsHandler.SearchDataset)
|
2026-05-20 20:32:06 +08:00
|
|
|
datasets.GET("/metadata/flattened", r.datasetsHandler.ListMetadataFlattened)
|
2026-06-09 19:27:47 +08:00
|
|
|
datasets.GET("/:dataset_id/metadata/summary", r.documentHandler.MetadataSummaryByDataset)
|
2026-05-15 14:00:45 +08:00
|
|
|
|
2026-06-01 06:23:44 +03:00
|
|
|
// Dataset ingestion logs
|
|
|
|
|
datasets.GET("/:dataset_id/ingestions/summary", r.datasetsHandler.GetIngestionSummary)
|
|
|
|
|
datasets.GET("/:dataset_id/ingestions", r.datasetsHandler.ListIngestionLogs)
|
|
|
|
|
datasets.GET("/:dataset_id/ingestions/:log_id", r.datasetsHandler.GetIngestionLog)
|
|
|
|
|
|
2026-06-02 13:24:28 +08:00
|
|
|
// Metadata Config
|
|
|
|
|
datasets.GET("/:dataset_id/metadata/config", r.datasetsHandler.GetMetadataConfig)
|
|
|
|
|
datasets.PUT("/:dataset_id/metadata/config", r.datasetsHandler.UpdateMetadataConfig)
|
|
|
|
|
|
2026-05-15 14:00:45 +08:00
|
|
|
// Dataset documents
|
|
|
|
|
datasets.GET("/:dataset_id/documents", r.documentHandler.ListDocuments)
|
2026-06-25 13:36:49 +08:00
|
|
|
datasets.POST("/:dataset_id/documents", r.documentHandler.UploadDocuments)
|
2026-06-08 11:37:06 +08:00
|
|
|
datasets.GET("/:dataset_id/documents/:document_id", r.documentHandler.DownloadDocument)
|
2026-06-18 11:08:47 +08:00
|
|
|
datasets.PATCH("/:dataset_id/documents/:document_id", r.documentHandler.UpdateDatasetDocument)
|
2026-06-03 20:55:53 +08:00
|
|
|
datasets.DELETE("/:dataset_id/documents", r.documentHandler.DeleteDocuments)
|
2026-06-25 14:15:29 +08:00
|
|
|
datasets.POST("/:dataset_id/documents/:document_id/chunks", r.chunkHandler.AddChunk)
|
2026-05-20 20:32:06 +08:00
|
|
|
|
|
|
|
|
// Dataset document chunk
|
2026-06-23 18:50:36 +08:00
|
|
|
datasets.GET("/:dataset_id/documents/:document_id/chunks", r.chunkHandler.ListChunks)
|
|
|
|
|
datasets.PATCH("/:dataset_id/documents/:document_id/chunks", r.chunkHandler.SwitchChunks)
|
2026-05-20 20:32:06 +08:00
|
|
|
datasets.GET("/:dataset_id/documents/:document_id/chunks/:chunk_id", r.chunkHandler.Get)
|
2026-06-22 18:14:01 +08:00
|
|
|
datasets.POST("/:dataset_id/chunks", r.chunkHandler.Parse)
|
2026-06-23 18:50:36 +08:00
|
|
|
datasets.PATCH("/:dataset_id/documents/:document_id/chunks/:chunk_id", r.chunkHandler.UpdateChunk)
|
2026-06-12 14:56:44 +08:00
|
|
|
datasets.POST("/:dataset_id/documents/parse", r.documentHandler.StartIngestionTask)
|
|
|
|
|
datasets.GET("/ingestion/tasks", r.documentHandler.ListIngestionTasks)
|
|
|
|
|
datasets.PUT("/ingestion/tasks", r.documentHandler.StopIngestionTasks)
|
|
|
|
|
datasets.DELETE("/ingestion/tasks", r.documentHandler.RemoveIngestionTasks)
|
|
|
|
|
//datasets.POST("/:dataset_id/documents/parse", r.documentHandler.ParseDocuments)
|
|
|
|
|
//datasets.POST("/:dataset_id/documents/stop", r.documentHandler.StopParseDocuments)
|
2026-06-24 19:43:18 +08:00
|
|
|
datasets.DELETE("/:dataset_id/chunks", r.chunkHandler.StopParsing)
|
2026-05-25 19:15:07 +08:00
|
|
|
datasets.DELETE("/:dataset_id/documents/:document_id/chunks", r.chunkHandler.RemoveChunks)
|
2026-06-10 09:57:11 +08:00
|
|
|
datasets.PUT("/:dataset_id/documents/:document_id/metadata/config", r.datasetsHandler.UpdateDocumentMetadataConfig)
|
2026-06-24 14:52:47 +08:00
|
|
|
datasets.POST("/:dataset_id/metadata/update", r.documentHandler.MetadataBatchUpdate)
|
|
|
|
|
datasets.PATCH("/:dataset_id/documents/metadatas", r.documentHandler.UpdateDocumentMetadatas)
|
2026-03-19 20:48:32 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-07 17:14:22 +08:00
|
|
|
// Search routes
|
|
|
|
|
searches := v1.Group("/searches")
|
|
|
|
|
{
|
|
|
|
|
searches.GET("", r.searchHandler.ListSearches)
|
|
|
|
|
searches.POST("", r.searchHandler.CreateSearch)
|
|
|
|
|
searches.GET("/:search_id", r.searchHandler.GetSearch)
|
|
|
|
|
searches.PUT("/:search_id", r.searchHandler.UpdateSearch)
|
|
|
|
|
searches.DELETE("/:search_id", r.searchHandler.DeleteSearch)
|
2026-06-29 20:07:12 +08:00
|
|
|
searches.POST("/:search_id/completion", r.searchHandler.Completion)
|
|
|
|
|
searches.POST("/:search_id/completions", r.searchHandler.Completion)
|
2026-05-07 17:14:22 +08:00
|
|
|
}
|
|
|
|
|
|
2026-07-03 17:00:43 +08:00
|
|
|
files := v1.Group("/files")
|
2026-05-07 17:14:22 +08:00
|
|
|
{
|
2026-07-03 17:00:43 +08:00
|
|
|
files.POST("", r.fileHandler.UploadFile)
|
|
|
|
|
files.GET("", r.fileHandler.ListFiles)
|
|
|
|
|
files.DELETE("", r.fileHandler.DeleteFiles)
|
|
|
|
|
files.POST("/move", r.fileHandler.MoveFiles)
|
|
|
|
|
files.POST("/link-to-datasets", r.fileHandler.LinkToDatasets)
|
|
|
|
|
files.GET("/:id/ancestors", r.fileHandler.GetFileAncestors)
|
|
|
|
|
files.GET("/:id/parent", r.fileHandler.GetParentFolder)
|
|
|
|
|
files.GET("/:id", r.fileHandler.Download)
|
|
|
|
|
files.GET("/:id/versions", r.fileCommitHandler.GetFileVersionHistory)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// File routes
|
|
|
|
|
file := authorized.Group("/v1/file")
|
|
|
|
|
{
|
|
|
|
|
file.GET("/root_folder", r.fileHandler.GetRootFolder)
|
|
|
|
|
file.GET("/parent_folder", r.fileHandler.GetParentFolder)
|
|
|
|
|
file.GET("/all_parent_folder", r.fileHandler.GetAllParentFolders)
|
2026-05-07 17:14:22 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-15 11:19:56 +08:00
|
|
|
// File commit routes — /folders/ takes folder_id directly
|
|
|
|
|
commitFolders := v1.Group("/folders")
|
|
|
|
|
{
|
|
|
|
|
commitFolders.POST("/:folder_id/commits", r.fileCommitHandler.CreateCommit)
|
|
|
|
|
commitFolders.GET("/:folder_id/commits", r.fileCommitHandler.ListCommits)
|
|
|
|
|
commitFolders.GET("/:folder_id/commits/diff", r.fileCommitHandler.DiffCommits)
|
|
|
|
|
commitFolders.GET("/:folder_id/commits/:commit_id", r.fileCommitHandler.GetCommit)
|
|
|
|
|
commitFolders.GET("/:folder_id/commits/:commit_id/files", r.fileCommitHandler.ListCommitFiles)
|
|
|
|
|
commitFolders.GET("/:folder_id/commits/:commit_id/tree", r.fileCommitHandler.GetCommitTree)
|
|
|
|
|
commitFolders.GET("/:folder_id/commits/:commit_id/files/:file_id/content", r.fileCommitHandler.GetCommitFileContent)
|
|
|
|
|
commitFolders.GET("/:folder_id/changes", r.fileCommitHandler.GetUncommittedChanges)
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-18 17:57:07 +08:00
|
|
|
// /workspace/{workspace_id}/commits — alias for /folders/ (workspace_id == folder_id)
|
|
|
|
|
commitWorkspace := v1.Group("/workspace")
|
|
|
|
|
{
|
|
|
|
|
commitWorkspace.POST("/:folder_id/commits", r.fileCommitHandler.CreateCommit)
|
|
|
|
|
commitWorkspace.GET("/:folder_id/commits", r.fileCommitHandler.ListCommits)
|
|
|
|
|
commitWorkspace.GET("/:folder_id/commits/diff", r.fileCommitHandler.DiffCommits)
|
|
|
|
|
commitWorkspace.GET("/:folder_id/commits/:commit_id", r.fileCommitHandler.GetCommit)
|
|
|
|
|
commitWorkspace.GET("/:folder_id/commits/:commit_id/files", r.fileCommitHandler.ListCommitFiles)
|
|
|
|
|
commitWorkspace.GET("/:folder_id/commits/:commit_id/tree", r.fileCommitHandler.GetCommitTree)
|
|
|
|
|
commitWorkspace.GET("/:folder_id/commits/:commit_id/files/:file_id/content", r.fileCommitHandler.GetCommitFileContent)
|
|
|
|
|
commitWorkspace.GET("/:folder_id/changes", r.fileCommitHandler.GetUncommittedChanges)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// /datasets/{dataset_id}/commits — resolve dataset_id → folder_id via middleware
|
|
|
|
|
commitDatasets := v1.Group("/datasets/:dataset_id")
|
|
|
|
|
commitDatasets.Use(handler.CommitFolderResolver(r.fileCommitHandler, "datasets", "dataset_id"))
|
|
|
|
|
{
|
|
|
|
|
commitDatasets.POST("/commits", r.fileCommitHandler.CreateCommit)
|
|
|
|
|
commitDatasets.GET("/commits", r.fileCommitHandler.ListCommits)
|
|
|
|
|
commitDatasets.GET("/commits/diff", r.fileCommitHandler.DiffCommits)
|
|
|
|
|
commitDatasets.GET("/commits/:commit_id", r.fileCommitHandler.GetCommit)
|
|
|
|
|
commitDatasets.GET("/commits/:commit_id/files", r.fileCommitHandler.ListCommitFiles)
|
|
|
|
|
commitDatasets.GET("/commits/:commit_id/tree", r.fileCommitHandler.GetCommitTree)
|
|
|
|
|
commitDatasets.GET("/commits/:commit_id/files/:file_id/content", r.fileCommitHandler.GetCommitFileContent)
|
|
|
|
|
commitDatasets.GET("/changes", r.fileCommitHandler.GetUncommittedChanges)
|
|
|
|
|
}
|
2026-06-15 11:19:56 +08:00
|
|
|
|
2026-03-27 09:49:50 +08:00
|
|
|
// Memory routes
|
|
|
|
|
memory := v1.Group("/memories")
|
|
|
|
|
{
|
|
|
|
|
memory.POST("", r.memoryHandler.CreateMemory)
|
|
|
|
|
memory.PUT("/:memory_id", r.memoryHandler.UpdateMemory)
|
|
|
|
|
memory.DELETE("/:memory_id", r.memoryHandler.DeleteMemory)
|
|
|
|
|
memory.GET("", r.memoryHandler.ListMemories)
|
|
|
|
|
memory.GET("/:memory_id/config", r.memoryHandler.GetMemoryConfig)
|
|
|
|
|
memory.GET("/:memory_id", r.memoryHandler.GetMemoryMessages)
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-10 20:27:35 +07:00
|
|
|
// Message routes
|
|
|
|
|
message := v1.Group("/messages")
|
|
|
|
|
{
|
2026-06-25 19:07:34 +08:00
|
|
|
message.GET("", r.memoryHandler.GetMessages)
|
2026-06-26 19:21:52 +08:00
|
|
|
message.POST("", r.memoryHandler.AddMessage)
|
2026-06-10 20:27:35 +07:00
|
|
|
message.DELETE("/:memory_message", r.memoryHandler.ForgetMessage)
|
2026-06-18 17:57:07 +08:00
|
|
|
message.PUT("/:memory_message", r.memoryHandler.UpdateMessage)
|
2026-06-25 19:07:34 +08:00
|
|
|
message.GET("/:memory_message/content", r.memoryHandler.GetMessageContent)
|
|
|
|
|
message.GET("/search", r.memoryHandler.SearchMessage)
|
2026-06-10 20:27:35 +07:00
|
|
|
}
|
2026-04-30 12:36:03 +08:00
|
|
|
|
2026-05-07 17:14:22 +08:00
|
|
|
// Skill search routes
|
|
|
|
|
skills := v1.Group("/skills")
|
2026-04-14 15:19:31 +08:00
|
|
|
{
|
2026-05-07 17:14:22 +08:00
|
|
|
// Skill Space management
|
|
|
|
|
skills.GET("/spaces", r.skillSearchHandler.ListSpaces)
|
|
|
|
|
skills.POST("/spaces", r.skillSearchHandler.CreateSpace)
|
|
|
|
|
skills.GET("/spaces/:space_id", r.skillSearchHandler.GetSpace)
|
|
|
|
|
skills.PUT("/spaces/:space_id", r.skillSearchHandler.UpdateSpace)
|
|
|
|
|
skills.DELETE("/spaces/:space_id", r.skillSearchHandler.DeleteSpace)
|
|
|
|
|
skills.GET("/space/by-folder", r.skillSearchHandler.GetSpaceByFolder)
|
2026-04-07 19:07:47 +08:00
|
|
|
|
2026-05-07 17:14:22 +08:00
|
|
|
// Skill search config
|
|
|
|
|
skills.GET("/config", r.skillSearchHandler.GetConfig)
|
|
|
|
|
skills.POST("/config", r.skillSearchHandler.UpdateConfig)
|
2026-04-30 12:36:03 +08:00
|
|
|
|
2026-05-07 17:14:22 +08:00
|
|
|
// Skill search and indexing
|
|
|
|
|
skills.POST("/search", r.skillSearchHandler.Search)
|
|
|
|
|
skills.POST("/index", r.skillSearchHandler.IndexSkills)
|
|
|
|
|
skills.DELETE("/index", r.skillSearchHandler.DeleteSkillIndex)
|
|
|
|
|
skills.POST("/reindex", r.skillSearchHandler.Reindex)
|
2026-04-02 20:21:04 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-31 18:42:12 +08:00
|
|
|
// provider pool route group
|
|
|
|
|
provider := v1.Group("/providers")
|
|
|
|
|
{
|
|
|
|
|
provider.GET("/", r.providerHandler.ListProviders)
|
2026-04-17 09:55:25 +08:00
|
|
|
provider.PUT("/", r.providerHandler.AddProvider)
|
2026-03-31 18:42:12 +08:00
|
|
|
provider.GET("/:provider_name", r.providerHandler.ShowProvider)
|
2026-04-02 20:20:35 +08:00
|
|
|
provider.DELETE("/:provider_name", r.providerHandler.DeleteProvider)
|
2026-03-31 18:42:12 +08:00
|
|
|
provider.GET("/:provider_name/models", r.providerHandler.ListModels)
|
|
|
|
|
provider.GET("/:provider_name/models/:model_name", r.providerHandler.ShowModel)
|
2026-04-02 20:20:35 +08:00
|
|
|
provider.POST("/:provider_name/instances", r.providerHandler.CreateProviderInstance)
|
|
|
|
|
provider.GET("/:provider_name/instances", r.providerHandler.ListProviderInstances)
|
|
|
|
|
provider.GET("/:provider_name/instances/:instance_name", r.providerHandler.ShowProviderInstance)
|
2026-04-21 21:31:50 +08:00
|
|
|
provider.GET("/:provider_name/instances/:instance_name/balance", r.providerHandler.ShowInstanceBalance)
|
2026-06-02 19:32:41 +08:00
|
|
|
provider.GET("/:provider_name/instances/:instance_name/connection", r.providerHandler.CheckInstanceConnection)
|
feat(agent): Go port — canvas engine, 22 components, DSL v2, 13 endpoints (#15952)
Ports the agent canvas subsystem from Python to Go.
## What's included
### Canvas Engine (Phase 0/1)
- State engine, scheduler, variable resolver, Redis checkpoint store,
cancel protocol
- **209 tests** across canvas / component / io packages
### 22 Components (P0–P4)
| Tier | Components |
|---|---|
| P0 T1+T2+T3 | LLM, Agent, ExitLoop, Switch, Categorize, Begin,
Message, Invoke |
| P1 T3 | VariableAggregator, VariableAssigner, StringTransform,
ListOperations, DataOperations |
| P2 T3 | Iteration, IterationItem, Loop, LoopItem |
| P3 T3 | UserFillUp, Fillup |
| P4 T5 | Browser, ExcelProcessor, DocsGenerator |
### DSL v2 Schema (Phase 2.5)
- Typed v2 in-memory model with v1-to-v2 auto-detect converter
- v1 legacy field stripping per plan §2.11.7
### HTTP Endpoints & Bug Fixes (Plans PR1–PR3)
- **DELETE SQL bug fix**: gorm v2 `Where("id = ?", id).Delete(...)`
pattern
- **CreateAgent validation**: title/DSL required, duplicate check, 103
envelope
- **13 new endpoints**: templates, prompts, tags, sessions CRUD,
chat/completions (SSE + non-stream stubs), rerun, test_db_connection,
logs, webhook/logs
- **756 Go unit tests** (745 → 756, +18)
- **17 → 0 Python integration test failures** (test_agents.py +
test_session_management/)
### Tools
21 eino tools: HTTPHelper, search tools, financial/data tools, mandatory
stubs
### Infrastructure
OTel observability, NATS message queue, DeepDoc gRPC client, SSRF
guards, IDOR mitigation
2026-06-12 22:58:28 +08:00
|
|
|
provider.POST("/:provider_name/connection", r.providerHandler.CheckConnection)
|
2026-05-15 12:29:52 +08:00
|
|
|
provider.GET("/:provider_name/instances/:instance_name/tasks", r.providerHandler.ListTasks)
|
|
|
|
|
provider.GET("/:provider_name/instances/:instance_name/tasks/:task_id", r.providerHandler.ShowTask)
|
2026-04-02 20:20:35 +08:00
|
|
|
provider.PUT("/:provider_name/instances/:instance_name", r.providerHandler.AlterProviderInstance)
|
2026-04-17 09:55:25 +08:00
|
|
|
provider.DELETE("/:provider_name/instances", r.providerHandler.DropProviderInstance)
|
2026-04-02 20:20:35 +08:00
|
|
|
provider.GET("/:provider_name/instances/:instance_name/models", r.providerHandler.ListInstanceModels)
|
2026-04-28 12:59:01 +08:00
|
|
|
provider.PATCH("/:provider_name/instances/:instance_name/models/*model_name", r.providerHandler.EnableOrDisableModel)
|
2026-06-03 15:26:46 +08:00
|
|
|
provider.POST("/:provider_name/instances/:instance_name/models", r.providerHandler.AddModel)
|
2026-04-29 19:18:49 +08:00
|
|
|
provider.DELETE("/:provider_name/instances/:instance_name/models", r.providerHandler.DropInstanceModels)
|
2026-07-01 15:52:52 +08:00
|
|
|
v1.POST("/chat/to_model", r.providerHandler.ChatToModel)
|
2026-05-09 17:41:54 +08:00
|
|
|
v1.POST("/embeddings", r.providerHandler.EmbedText)
|
|
|
|
|
v1.POST("/rerank", r.providerHandler.RerankDocument)
|
2026-05-12 17:17:44 +08:00
|
|
|
v1.POST("/audio/transcriptions", r.providerHandler.TranscribeAudio)
|
|
|
|
|
v1.POST("/audio/speech", r.providerHandler.AudioSpeech)
|
|
|
|
|
v1.POST("/file/ocr", r.providerHandler.OCRFile)
|
2026-05-15 12:29:52 +08:00
|
|
|
v1.POST("/file/parse", r.providerHandler.ParseFile)
|
2026-03-31 18:42:12 +08:00
|
|
|
}
|
2026-04-07 19:07:47 +08:00
|
|
|
|
2026-04-17 18:05:33 +08:00
|
|
|
model := v1.Group("/models")
|
|
|
|
|
{
|
feat(agent): Go port — canvas engine, 22 components, DSL v2, 13 endpoints (#15952)
Ports the agent canvas subsystem from Python to Go.
## What's included
### Canvas Engine (Phase 0/1)
- State engine, scheduler, variable resolver, Redis checkpoint store,
cancel protocol
- **209 tests** across canvas / component / io packages
### 22 Components (P0–P4)
| Tier | Components |
|---|---|
| P0 T1+T2+T3 | LLM, Agent, ExitLoop, Switch, Categorize, Begin,
Message, Invoke |
| P1 T3 | VariableAggregator, VariableAssigner, StringTransform,
ListOperations, DataOperations |
| P2 T3 | Iteration, IterationItem, Loop, LoopItem |
| P3 T3 | UserFillUp, Fillup |
| P4 T5 | Browser, ExcelProcessor, DocsGenerator |
### DSL v2 Schema (Phase 2.5)
- Typed v2 in-memory model with v1-to-v2 auto-detect converter
- v1 legacy field stripping per plan §2.11.7
### HTTP Endpoints & Bug Fixes (Plans PR1–PR3)
- **DELETE SQL bug fix**: gorm v2 `Where("id = ?", id).Delete(...)`
pattern
- **CreateAgent validation**: title/DSL required, duplicate check, 103
envelope
- **13 new endpoints**: templates, prompts, tags, sessions CRUD,
chat/completions (SSE + non-stream stubs), rerun, test_db_connection,
logs, webhook/logs
- **756 Go unit tests** (745 → 756, +18)
- **17 → 0 Python integration test failures** (test_agents.py +
test_session_management/)
### Tools
21 eino tools: HTTPHelper, search tools, financial/data tools, mandatory
stubs
### Infrastructure
OTel observability, NATS message queue, DeepDoc gRPC client, SSRF
guards, IDOR mitigation
2026-06-12 22:58:28 +08:00
|
|
|
// GET /models returns the tenant's added models across
|
2026-07-03 17:00:43 +08:00
|
|
|
// all instances. Front-end useFetchAllAddedModels consumes this.
|
feat(agent): Go port — canvas engine, 22 components, DSL v2, 13 endpoints (#15952)
Ports the agent canvas subsystem from Python to Go.
## What's included
### Canvas Engine (Phase 0/1)
- State engine, scheduler, variable resolver, Redis checkpoint store,
cancel protocol
- **209 tests** across canvas / component / io packages
### 22 Components (P0–P4)
| Tier | Components |
|---|---|
| P0 T1+T2+T3 | LLM, Agent, ExitLoop, Switch, Categorize, Begin,
Message, Invoke |
| P1 T3 | VariableAggregator, VariableAssigner, StringTransform,
ListOperations, DataOperations |
| P2 T3 | Iteration, IterationItem, Loop, LoopItem |
| P3 T3 | UserFillUp, Fillup |
| P4 T5 | Browser, ExcelProcessor, DocsGenerator |
### DSL v2 Schema (Phase 2.5)
- Typed v2 in-memory model with v1-to-v2 auto-detect converter
- v1 legacy field stripping per plan §2.11.7
### HTTP Endpoints & Bug Fixes (Plans PR1–PR3)
- **DELETE SQL bug fix**: gorm v2 `Where("id = ?", id).Delete(...)`
pattern
- **CreateAgent validation**: title/DSL required, duplicate check, 103
envelope
- **13 new endpoints**: templates, prompts, tags, sessions CRUD,
chat/completions (SSE + non-stream stubs), rerun, test_db_connection,
logs, webhook/logs
- **756 Go unit tests** (745 → 756, +18)
- **17 → 0 Python integration test failures** (test_agents.py +
test_session_management/)
### Tools
21 eino tools: HTTPHelper, search tools, financial/data tools, mandatory
stubs
### Infrastructure
OTel observability, NATS message queue, DeepDoc gRPC client, SSRF
guards, IDOR mitigation
2026-06-12 22:58:28 +08:00
|
|
|
model.GET("/", r.providerHandler.ListTenantAddedModels)
|
2026-04-17 18:05:33 +08:00
|
|
|
model.PATCH("/", r.tenantHandler.SetModels)
|
2026-07-03 17:00:43 +08:00
|
|
|
// Tenant default-model selection (used by the agent page's useFetchDefaultModels hook)
|
feat(agent): Go port — canvas engine, 22 components, DSL v2, 13 endpoints (#15952)
Ports the agent canvas subsystem from Python to Go.
## What's included
### Canvas Engine (Phase 0/1)
- State engine, scheduler, variable resolver, Redis checkpoint store,
cancel protocol
- **209 tests** across canvas / component / io packages
### 22 Components (P0–P4)
| Tier | Components |
|---|---|
| P0 T1+T2+T3 | LLM, Agent, ExitLoop, Switch, Categorize, Begin,
Message, Invoke |
| P1 T3 | VariableAggregator, VariableAssigner, StringTransform,
ListOperations, DataOperations |
| P2 T3 | Iteration, IterationItem, Loop, LoopItem |
| P3 T3 | UserFillUp, Fillup |
| P4 T5 | Browser, ExcelProcessor, DocsGenerator |
### DSL v2 Schema (Phase 2.5)
- Typed v2 in-memory model with v1-to-v2 auto-detect converter
- v1 legacy field stripping per plan §2.11.7
### HTTP Endpoints & Bug Fixes (Plans PR1–PR3)
- **DELETE SQL bug fix**: gorm v2 `Where("id = ?", id).Delete(...)`
pattern
- **CreateAgent validation**: title/DSL required, duplicate check, 103
envelope
- **13 new endpoints**: templates, prompts, tags, sessions CRUD,
chat/completions (SSE + non-stream stubs), rerun, test_db_connection,
logs, webhook/logs
- **756 Go unit tests** (745 → 756, +18)
- **17 → 0 Python integration test failures** (test_agents.py +
test_session_management/)
### Tools
21 eino tools: HTTPHelper, search tools, financial/data tools, mandatory
stubs
### Infrastructure
OTel observability, NATS message queue, DeepDoc gRPC client, SSRF
guards, IDOR mitigation
2026-06-12 22:58:28 +08:00
|
|
|
model.GET("/default", r.tenantHandler.GetDefaultModels)
|
|
|
|
|
model.PATCH("/default", r.tenantHandler.SetDefaultModels)
|
2026-04-17 18:05:33 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-08 21:38:15 +08:00
|
|
|
allModels := v1.Group("/all-models")
|
|
|
|
|
{
|
|
|
|
|
allModels.GET("", r.modelHandler.ListAllModels)
|
2026-06-26 15:36:01 +08:00
|
|
|
allModels.GET("/:model_name", r.modelHandler.ShowModel)
|
2026-06-08 21:38:15 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-28 05:40:54 -06:00
|
|
|
// Agent routes
|
|
|
|
|
agents := v1.Group("/agents")
|
feat(agent): Go port — canvas engine, 22 components, DSL v2, 13 endpoints (#15952)
Ports the agent canvas subsystem from Python to Go.
## What's included
### Canvas Engine (Phase 0/1)
- State engine, scheduler, variable resolver, Redis checkpoint store,
cancel protocol
- **209 tests** across canvas / component / io packages
### 22 Components (P0–P4)
| Tier | Components |
|---|---|
| P0 T1+T2+T3 | LLM, Agent, ExitLoop, Switch, Categorize, Begin,
Message, Invoke |
| P1 T3 | VariableAggregator, VariableAssigner, StringTransform,
ListOperations, DataOperations |
| P2 T3 | Iteration, IterationItem, Loop, LoopItem |
| P3 T3 | UserFillUp, Fillup |
| P4 T5 | Browser, ExcelProcessor, DocsGenerator |
### DSL v2 Schema (Phase 2.5)
- Typed v2 in-memory model with v1-to-v2 auto-detect converter
- v1 legacy field stripping per plan §2.11.7
### HTTP Endpoints & Bug Fixes (Plans PR1–PR3)
- **DELETE SQL bug fix**: gorm v2 `Where("id = ?", id).Delete(...)`
pattern
- **CreateAgent validation**: title/DSL required, duplicate check, 103
envelope
- **13 new endpoints**: templates, prompts, tags, sessions CRUD,
chat/completions (SSE + non-stream stubs), rerun, test_db_connection,
logs, webhook/logs
- **756 Go unit tests** (745 → 756, +18)
- **17 → 0 Python integration test failures** (test_agents.py +
test_session_management/)
### Tools
21 eino tools: HTTPHelper, search tools, financial/data tools, mandatory
stubs
### Infrastructure
OTel observability, NATS message queue, DeepDoc gRPC client, SSRF
guards, IDOR mitigation
2026-06-12 22:58:28 +08:00
|
|
|
RegisterAgentRoutes(agents, r.agentHandler)
|
2026-05-28 05:40:54 -06:00
|
|
|
|
2026-06-07 20:53:19 -07:00
|
|
|
// Plugin routes
|
|
|
|
|
plugin := v1.Group("/plugin")
|
|
|
|
|
{
|
|
|
|
|
plugin.GET("/tools", r.pluginHandler.ListLLMTools)
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-05 20:43:52 +08:00
|
|
|
// Component catalog — Phase 4 of
|
|
|
|
|
// port-rag-flow-pipeline-to-go.md. Optional
|
|
|
|
|
// ?category=ingestion,agent,shared filter; defaults to
|
|
|
|
|
// all categories. The data source is
|
|
|
|
|
// runtime.DefaultRegistry.
|
|
|
|
|
if r.componentsHandler != nil {
|
|
|
|
|
v1.GET("/components", r.componentsHandler.Get)
|
|
|
|
|
}
|
|
|
|
|
|
feat(agent): Go port — canvas engine, 22 components, DSL v2, 13 endpoints (#15952)
Ports the agent canvas subsystem from Python to Go.
## What's included
### Canvas Engine (Phase 0/1)
- State engine, scheduler, variable resolver, Redis checkpoint store,
cancel protocol
- **209 tests** across canvas / component / io packages
### 22 Components (P0–P4)
| Tier | Components |
|---|---|
| P0 T1+T2+T3 | LLM, Agent, ExitLoop, Switch, Categorize, Begin,
Message, Invoke |
| P1 T3 | VariableAggregator, VariableAssigner, StringTransform,
ListOperations, DataOperations |
| P2 T3 | Iteration, IterationItem, Loop, LoopItem |
| P3 T3 | UserFillUp, Fillup |
| P4 T5 | Browser, ExcelProcessor, DocsGenerator |
### DSL v2 Schema (Phase 2.5)
- Typed v2 in-memory model with v1-to-v2 auto-detect converter
- v1 legacy field stripping per plan §2.11.7
### HTTP Endpoints & Bug Fixes (Plans PR1–PR3)
- **DELETE SQL bug fix**: gorm v2 `Where("id = ?", id).Delete(...)`
pattern
- **CreateAgent validation**: title/DSL required, duplicate check, 103
envelope
- **13 new endpoints**: templates, prompts, tags, sessions CRUD,
chat/completions (SSE + non-stream stubs), rerun, test_db_connection,
logs, webhook/logs
- **756 Go unit tests** (745 → 756, +18)
- **17 → 0 Python integration test failures** (test_agents.py +
test_session_management/)
### Tools
21 eino tools: HTTPHelper, search tools, financial/data tools, mandatory
stubs
### Infrastructure
OTel observability, NATS message queue, DeepDoc gRPC client, SSRF
guards, IDOR mitigation
2026-06-12 22:58:28 +08:00
|
|
|
// Admin routes — Phase 6 per-tenant canvas runtime override.
|
|
|
|
|
// RegisterAdminRuntimeRoutes lives in admin_routes.go; a nil
|
|
|
|
|
// handler is tolerated and yields a no-op registration.
|
|
|
|
|
admin := v1.Group("/admin")
|
|
|
|
|
RegisterAdminRuntimeRoutes(admin, r.adminRuntimeHandler)
|
|
|
|
|
|
2026-07-03 17:00:43 +08:00
|
|
|
connectors := v1.Group("/connectors")
|
2026-05-18 16:57:14 +08:00
|
|
|
{
|
2026-07-03 17:00:43 +08:00
|
|
|
connectors.GET("/", r.connectorHandler.ListConnectors)
|
|
|
|
|
connectors.POST("/", r.connectorHandler.CreateConnector)
|
|
|
|
|
connectors.POST("/google/oauth/web/start", r.connectorHandler.StartGoogleWebOAuth)
|
|
|
|
|
connectors.POST("/google/oauth/web/result", r.connectorHandler.PollGoogleWebOAuthResult)
|
|
|
|
|
connectors.POST("/box/oauth/web/start", r.connectorHandler.StartBoxWebOAuth)
|
|
|
|
|
connectors.POST("/box/oauth/web/result", r.connectorHandler.PollBoxWebOAuthResult)
|
|
|
|
|
connectors.GET("/:connector_id", r.connectorHandler.GetConnector)
|
|
|
|
|
connectors.PATCH("/:connector_id", r.connectorHandler.UpdateConnector)
|
|
|
|
|
connectors.GET("/:connector_id/logs", r.connectorHandler.ListLogs)
|
|
|
|
|
connectors.DELETE("/:connector_id", r.connectorHandler.DeleteConnector)
|
|
|
|
|
connectors.POST("/:connector_id/rebuild", r.connectorHandler.RebuildConnector)
|
|
|
|
|
connectors.POST("/:connector_id/test", r.connectorHandler.TestConnector)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Connector routes
|
|
|
|
|
connector := authorized.Group("/v1/connector")
|
|
|
|
|
{
|
|
|
|
|
connector.GET("/list", r.connectorHandler.ListConnectors)
|
2026-05-26 20:07:55 -10:00
|
|
|
connector.GET("/:connector_id", r.connectorHandler.GetConnector)
|
2026-05-28 16:44:35 +08:00
|
|
|
connector.POST("/:connector_id/rebuild", r.connectorHandler.RebuildConnector)
|
2026-05-18 16:57:14 +08:00
|
|
|
}
|
|
|
|
|
|
2026-07-03 17:00:43 +08:00
|
|
|
// MCP server routes.
|
2026-06-04 23:25:09 -06:00
|
|
|
mcp := v1.Group("/mcp")
|
|
|
|
|
{
|
|
|
|
|
mcp.POST("/servers", r.mcpHandler.CreateMCPServer)
|
|
|
|
|
mcp.GET("/servers", r.mcpHandler.ListMCPServers)
|
2026-06-18 11:09:22 +08:00
|
|
|
mcp.GET("/servers/:mcp_id", r.mcpHandler.GetMCPServer)
|
2026-06-04 23:25:09 -06:00
|
|
|
mcp.PUT("/servers/:mcp_id", r.mcpHandler.UpdateMCPServer)
|
|
|
|
|
mcp.DELETE("/servers/:mcp_id", r.mcpHandler.DeleteMCPServer)
|
|
|
|
|
mcp.POST("/servers/import", r.mcpHandler.ImportMCPServers)
|
|
|
|
|
mcp.POST("/servers/:mcp_id/test", r.mcpHandler.TestMCPServer)
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-07 19:07:47 +08:00
|
|
|
system := v1.Group("/system")
|
|
|
|
|
{
|
2026-04-08 19:32:53 +08:00
|
|
|
system.GET("/configs", r.systemHandler.GetConfigs)
|
2026-05-29 10:12:12 +08:00
|
|
|
system.GET("/status", r.systemHandler.GetStatus)
|
2026-05-29 19:32:21 +08:00
|
|
|
system.GET("/stats", r.systemHandler.GetStats)
|
|
|
|
|
|
|
|
|
|
config := system.Group("/config")
|
2026-04-08 19:32:53 +08:00
|
|
|
{
|
2026-05-29 19:32:21 +08:00
|
|
|
config.GET("/log", r.systemHandler.GetLogLevel)
|
|
|
|
|
config.PUT("/log", r.systemHandler.SetLogLevel)
|
2026-04-08 19:32:53 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-25 20:36:50 +08:00
|
|
|
// Variables/Settings
|
|
|
|
|
system.GET("/variables", r.systemHandler.ListVariables)
|
|
|
|
|
system.PUT("/variables", r.systemHandler.SetVariable)
|
2026-06-26 13:51:56 +08:00
|
|
|
system.GET("/variables/:var_name", r.systemHandler.ShowVariable)
|
2026-06-25 20:36:50 +08:00
|
|
|
|
|
|
|
|
// Environments
|
|
|
|
|
system.GET("/environments", r.systemHandler.ListEnvironments)
|
|
|
|
|
|
2026-04-08 19:32:53 +08:00
|
|
|
tokens := system.Group("/tokens")
|
|
|
|
|
{
|
|
|
|
|
// list tokens /api/v1/system/tokens GET
|
2026-06-24 16:50:40 +08:00
|
|
|
tokens.GET("", r.systemHandler.ListAPIKeys)
|
2026-04-08 19:32:53 +08:00
|
|
|
// create token /api/v1/system/tokens POST
|
2026-06-24 16:50:40 +08:00
|
|
|
tokens.POST("", r.systemHandler.CreateKey)
|
|
|
|
|
// delete token /api/v1/system/tokens/:key DELETE
|
|
|
|
|
tokens.DELETE("/:key", r.systemHandler.DeleteKey)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
keys := system.Group("/keys")
|
|
|
|
|
{
|
|
|
|
|
// list keys /api/v1/system/keys GET
|
|
|
|
|
keys.GET("", r.systemHandler.ListAPIKeys)
|
|
|
|
|
// create key /api/v1/system/keys POST
|
|
|
|
|
keys.POST("", r.systemHandler.CreateKey)
|
|
|
|
|
// delete key /api/v1/system/keys/:key DELETE
|
|
|
|
|
keys.DELETE("/:key", r.systemHandler.DeleteKey)
|
2026-04-08 19:32:53 +08:00
|
|
|
}
|
2026-04-07 19:07:47 +08:00
|
|
|
}
|
2026-03-04 19:17:16 +08:00
|
|
|
|
2026-07-03 17:00:43 +08:00
|
|
|
// Document routes
|
|
|
|
|
doc := v1.Group("/document")
|
2026-03-11 11:23:13 +08:00
|
|
|
{
|
2026-07-03 17:00:43 +08:00
|
|
|
doc.POST("/list", r.documentHandler.ListDocuments)
|
|
|
|
|
doc.POST("/metadata/summary", r.documentHandler.MetadataSummary)
|
|
|
|
|
doc.POST("/set_meta", r.documentHandler.SetMeta)
|
|
|
|
|
doc.POST("/delete_meta", r.documentHandler.DeleteMeta) // Internal API only for GO
|
2026-03-11 11:23:13 +08:00
|
|
|
}
|
2026-03-26 11:54:10 +08:00
|
|
|
|
2026-07-03 17:00:43 +08:00
|
|
|
// Chunk routes
|
|
|
|
|
chunk := v1.Group("/chunk")
|
|
|
|
|
{
|
|
|
|
|
chunk.POST("/list", r.chunkHandler.List)
|
|
|
|
|
chunk.POST("/update", r.chunkHandler.UpdateChunk) // Internal API only for GO
|
|
|
|
|
}
|
2026-06-22 18:16:15 +08:00
|
|
|
|
2026-07-03 17:00:43 +08:00
|
|
|
// Chat Channel
|
|
|
|
|
chanChannel := v1.Group("/chat-channels")
|
|
|
|
|
{
|
|
|
|
|
chanChannel.POST("", r.chatChannelHandler.CreateChatChannel)
|
|
|
|
|
chanChannel.GET("", r.chatChannelHandler.ListChatChannel)
|
|
|
|
|
chanChannel.GET("/:channel_id", r.chatChannelHandler.GetChatChannel)
|
|
|
|
|
chanChannel.PATCH("/:channel_id", r.chatChannelHandler.UpdateChatChannel)
|
|
|
|
|
chanChannel.DELETE("/:channel_id", r.chatChannelHandler.DeleteChatChannel)
|
|
|
|
|
}
|
2026-06-25 19:25:55 +08:00
|
|
|
|
2026-07-03 17:00:43 +08:00
|
|
|
// Langfuse tracing keys
|
|
|
|
|
langfuse := v1.Group("/langfuse")
|
|
|
|
|
{
|
|
|
|
|
langfuse.POST("/api-key", r.langfuseHandler.SetAPIKey)
|
|
|
|
|
langfuse.PUT("/api-key", r.langfuseHandler.SetAPIKey)
|
|
|
|
|
langfuse.GET("/api-key", r.langfuseHandler.GetAPIKey)
|
|
|
|
|
langfuse.DELETE("/api-key", r.langfuseHandler.DeleteAPIKey)
|
|
|
|
|
}
|
2026-03-04 19:17:16 +08:00
|
|
|
|
2026-07-03 17:00:43 +08:00
|
|
|
// Dify retrieval routes
|
|
|
|
|
dify := v1.Group("/dify")
|
|
|
|
|
{
|
|
|
|
|
dify.POST("/retrieval", r.difyRetrievalHandler.Retrieval)
|
|
|
|
|
dify.GET("/retrieval", r.difyRetrievalHandler.Retrieval)
|
|
|
|
|
}
|
2026-03-11 11:23:13 +08:00
|
|
|
}
|
2026-06-05 21:16:25 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-04 19:17:16 +08:00
|
|
|
// Handle undefined routes
|
|
|
|
|
engine.NoRoute(handler.HandleNoRoute)
|
|
|
|
|
}
|