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"
|
|
|
|
|
|
|
|
|
|
"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
|
|
|
|
|
knowledgebaseHandler *handler.KnowledgebaseHandler
|
|
|
|
|
chunkHandler *handler.ChunkHandler
|
|
|
|
|
llmHandler *handler.LLMHandler
|
|
|
|
|
chatHandler *handler.ChatHandler
|
|
|
|
|
chatSessionHandler *handler.ChatSessionHandler
|
|
|
|
|
connectorHandler *handler.ConnectorHandler
|
|
|
|
|
searchHandler *handler.SearchHandler
|
|
|
|
|
fileHandler *handler.FileHandler
|
|
|
|
|
memoryHandler *handler.MemoryHandler
|
|
|
|
|
mcpHandler *handler.MCPHandler
|
|
|
|
|
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
|
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,
|
2026-05-20 20:32:06 +08:00
|
|
|
knowledgebaseHandler *handler.KnowledgebaseHandler,
|
2026-03-04 19:17:16 +08:00
|
|
|
chunkHandler *handler.ChunkHandler,
|
|
|
|
|
llmHandler *handler.LLMHandler,
|
|
|
|
|
chatHandler *handler.ChatHandler,
|
|
|
|
|
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-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-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,
|
|
|
|
|
knowledgebaseHandler: knowledgebaseHandler,
|
|
|
|
|
chunkHandler: chunkHandler,
|
|
|
|
|
llmHandler: llmHandler,
|
|
|
|
|
chatHandler: chatHandler,
|
|
|
|
|
chatSessionHandler: chatSessionHandler,
|
|
|
|
|
connectorHandler: connectorHandler,
|
|
|
|
|
searchHandler: searchHandler,
|
|
|
|
|
fileHandler: fileHandler,
|
|
|
|
|
memoryHandler: memoryHandler,
|
|
|
|
|
mcpHandler: mcpHandler,
|
|
|
|
|
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,
|
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.
|
|
|
|
|
engine.Use(gin.Logger())
|
|
|
|
|
|
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-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
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
// Document images are embedded directly in pages and match Python's public route.
|
|
|
|
|
apiNoAuth.GET("/documents/images/:image_id", r.documentHandler.GetDocumentImage)
|
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)
|
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-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-05-18 16:57:14 +08:00
|
|
|
v1.GET("/tenant/list", r.tenantHandler.TenantList)
|
|
|
|
|
|
2026-03-11 11:23:13 +08:00
|
|
|
// Document routes
|
|
|
|
|
documents := v1.Group("/documents")
|
|
|
|
|
{
|
|
|
|
|
documents.POST("", r.documentHandler.CreateDocument)
|
|
|
|
|
documents.GET("", r.documentHandler.ListDocuments)
|
2026-06-08 11:37:06 +08:00
|
|
|
documents.GET("/artifact/:filename", r.documentHandler.GetDocumentArtifact)
|
|
|
|
|
documents.GET("/:id/preview", r.documentHandler.GetDocumentPreview)
|
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-05-07 17:14:22 +08:00
|
|
|
// Chat routes
|
|
|
|
|
chats := v1.Group("/chats")
|
|
|
|
|
{
|
|
|
|
|
chats.GET("", r.chatHandler.ListChats)
|
|
|
|
|
chats.GET("/:chat_id", r.chatHandler.GetChat)
|
|
|
|
|
chats.GET("/:chat_id/sessions", r.chatSessionHandler.ListChatSessions)
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-04 19:13:58 +08:00
|
|
|
// Searchbot routes
|
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
|
|
|
v1.POST("/searchbots/related_questions", r.searchBotHandler.Handle)
|
|
|
|
|
v1.POST("/searchbots/retrieval_test", r.searchBotHandler.RetrievalTest)
|
2026-06-09 22:48:50 +08:00
|
|
|
v1.POST("/searchbots/ask", r.searchBotHandler.Ask)
|
2026-06-04 19:13:58 +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-05-12 17:16:48 +08:00
|
|
|
datasets.GET("/:dataset_id", r.datasetsHandler.GetDataset)
|
2026-05-18 20:02:53 +08:00
|
|
|
datasets.GET("/:dataset_id/graph", r.datasetsHandler.GetKnowledgeGraph)
|
2026-05-20 20:32:06 +08:00
|
|
|
datasets.DELETE("/:dataset_id/tags", r.datasetsHandler.RemoveTags)
|
2026-05-18 20:02:53 +08:00
|
|
|
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-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-08 11:37:06 +08:00
|
|
|
datasets.GET("/:dataset_id/documents/:document_id", r.documentHandler.DownloadDocument)
|
2026-06-03 20:55:53 +08:00
|
|
|
datasets.DELETE("/:dataset_id/documents", r.documentHandler.DeleteDocuments)
|
2026-05-20 20:32:06 +08:00
|
|
|
|
|
|
|
|
// Dataset document chunk
|
|
|
|
|
datasets.GET("/:dataset_id/documents/:document_id/chunks/:chunk_id", r.chunkHandler.Get)
|
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-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-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)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
file := v1.Group("/files")
|
|
|
|
|
{
|
|
|
|
|
file.POST("", r.fileHandler.UploadFile)
|
|
|
|
|
file.GET("", r.fileHandler.ListFiles)
|
|
|
|
|
file.DELETE("", r.fileHandler.DeleteFiles)
|
|
|
|
|
file.POST("/move", r.fileHandler.MoveFiles)
|
2026-06-10 01:46:55 -07:00
|
|
|
file.POST("/link-to-datasets", r.fileHandler.LinkToDatasets)
|
2026-05-07 17:14:22 +08:00
|
|
|
file.GET("/:id/ancestors", r.fileHandler.GetFileAncestors)
|
2026-05-18 16:57:14 +08:00
|
|
|
file.GET("/:id/parent", r.fileHandler.GetParentFolder)
|
2026-05-07 17:14:22 +08:00
|
|
|
file.GET("/:id", r.fileHandler.Download)
|
2026-06-15 11:19:56 +08:00
|
|
|
file.GET("/:id/versions", r.fileCommitHandler.GetFileVersionHistory)
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// /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-03-11 11:23:13 +08:00
|
|
|
// Author routes
|
|
|
|
|
authors := v1.Group("/authors")
|
|
|
|
|
{
|
|
|
|
|
authors.GET("/:author_id/documents", r.documentHandler.GetDocumentsByAuthorID)
|
|
|
|
|
}
|
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")
|
|
|
|
|
{
|
|
|
|
|
message.DELETE("/:memory_message", r.memoryHandler.ForgetMessage)
|
|
|
|
|
}
|
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
|
|
|
// Python's /providers/<name>/connection is POST — see
|
|
|
|
|
// api/apps/restful_apis/provider_api.py:359. The web front-end
|
|
|
|
|
// posts {api_key, base_url, region, model_info} there
|
|
|
|
|
// (web/src/services/llm-service.ts:45-48 method: 'post'). The
|
|
|
|
|
// Go handler body is already POST-shaped (ShouldBindJSON
|
|
|
|
|
// against CheckConnectionRequest), so the only thing missing
|
|
|
|
|
// was the routing method.
|
|
|
|
|
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-04-29 11:45:06 +08:00
|
|
|
v1.POST("/chat/completions", 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
|
|
|
|
|
// all instances, matching Python's
|
|
|
|
|
// models_api_service.list_tenant_added_models. Front-end
|
|
|
|
|
// useFetchAllAddedModels consumes this. Routed to the
|
|
|
|
|
// provider handler because that's where the
|
|
|
|
|
// modelProviderService is wired.
|
|
|
|
|
model.GET("/", r.providerHandler.ListTenantAddedModels)
|
2026-06-15 10:10:14 +08:00
|
|
|
|
|
|
|
|
// TODO: list default models?
|
|
|
|
|
//model.GET("/", r.tenantHandler.GetModels)
|
2026-04-17 18:05:33 +08:00
|
|
|
model.PATCH("/", r.tenantHandler.SetModels)
|
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
|
|
|
// Tenant default-model selection (used by the agent
|
|
|
|
|
// page's useFetchDefaultModels hook). Mirrors the
|
|
|
|
|
// Python contract at api/apps/restful_apis/models_api.py:84.
|
|
|
|
|
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-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)
|
|
|
|
|
}
|
|
|
|
|
|
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-05-18 16:57:14 +08:00
|
|
|
connector := v1.Group("/connectors")
|
|
|
|
|
{
|
|
|
|
|
connector.GET("/", r.connectorHandler.ListConnectors)
|
2026-05-27 15:54:11 +08:00
|
|
|
connector.POST("/", r.connectorHandler.CreateConnector)
|
2026-06-03 20:08:55 +08:00
|
|
|
connector.POST("/google/oauth/web/start", r.connectorHandler.StartGoogleWebOAuth)
|
|
|
|
|
connector.POST("/google/oauth/web/result", r.connectorHandler.PollGoogleWebOAuthResult)
|
2026-05-26 20:07:55 -10:00
|
|
|
connector.GET("/:connector_id", r.connectorHandler.GetConnector)
|
2026-05-28 16:44:35 +08:00
|
|
|
connector.GET("/:connector_id/logs", r.connectorHandler.ListLogs)
|
|
|
|
|
connector.DELETE("/:connector_id", r.connectorHandler.DeleteConnector)
|
|
|
|
|
connector.POST("/:connector_id/rebuild", r.connectorHandler.RebuildConnector)
|
2026-05-28 05:40:15 -06:00
|
|
|
connector.POST("/:connector_id/test", r.connectorHandler.TestConnector)
|
2026-05-18 16:57:14 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-04 23:25:09 -06:00
|
|
|
// MCP server routes. Per-server CRUD ships via separate PRs that
|
|
|
|
|
// share the same handler/service: GET list (#15253), GET by id
|
|
|
|
|
// (#15254), POST create (#15260, merged), PUT (#15261), DELETE
|
|
|
|
|
// (#15262, merged). This PR adds only the non-overlapping
|
|
|
|
|
// endpoints: import and test.
|
|
|
|
|
mcp := v1.Group("/mcp")
|
|
|
|
|
{
|
|
|
|
|
mcp.POST("/servers", r.mcpHandler.CreateMCPServer)
|
|
|
|
|
mcp.GET("/servers", r.mcpHandler.ListMCPServers)
|
|
|
|
|
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-05-29 19:32:21 +08:00
|
|
|
//log := system.Group("/log")
|
|
|
|
|
//{
|
|
|
|
|
// // /api/v1/system/log GET
|
|
|
|
|
// log.GET("", r.systemHandler.GetLogLevel)
|
|
|
|
|
// // /api/v1/system/log PUT
|
|
|
|
|
// log.PUT("", r.systemHandler.SetLogLevel)
|
|
|
|
|
//}
|
|
|
|
|
|
2026-04-08 19:32:53 +08:00
|
|
|
tokens := system.Group("/tokens")
|
|
|
|
|
{
|
|
|
|
|
// list tokens /api/v1/system/tokens GET
|
|
|
|
|
tokens.GET("", r.systemHandler.ListTokens)
|
|
|
|
|
// create token /api/v1/system/tokens POST
|
|
|
|
|
tokens.POST("", r.systemHandler.CreateToken)
|
|
|
|
|
// delete token /api/v1/system/tokens/:token DELETE
|
|
|
|
|
tokens.DELETE("/:token", r.systemHandler.DeleteToken)
|
|
|
|
|
}
|
2026-04-07 19:07:47 +08:00
|
|
|
}
|
2026-03-04 19:17:16 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-11 11:23:13 +08:00
|
|
|
// Knowledge base routes
|
2026-05-20 20:32:06 +08:00
|
|
|
kb := v1.Group("/kb")
|
2026-03-04 19:17:16 +08:00
|
|
|
{
|
2026-03-11 11:23:13 +08:00
|
|
|
kb.POST("/update", r.knowledgebaseHandler.UpdateKB)
|
|
|
|
|
kb.POST("/update_metadata_setting", r.knowledgebaseHandler.UpdateMetadataSetting)
|
|
|
|
|
kb.GET("/detail", r.knowledgebaseHandler.GetDetail)
|
|
|
|
|
kb.GET("/tags", r.knowledgebaseHandler.ListTagsFromKbs)
|
|
|
|
|
kb.GET("/get_meta", r.knowledgebaseHandler.GetMeta)
|
|
|
|
|
kb.GET("/basic_info", r.knowledgebaseHandler.GetBasicInfo)
|
|
|
|
|
|
|
|
|
|
// KB ID specific routes
|
|
|
|
|
kbByID := kb.Group("/:kb_id")
|
|
|
|
|
{
|
|
|
|
|
kbByID.GET("/tags", r.knowledgebaseHandler.ListTags)
|
|
|
|
|
kbByID.POST("/rename_tag", r.knowledgebaseHandler.RenameTag)
|
|
|
|
|
kbByID.GET("/knowledge_graph", r.knowledgebaseHandler.KnowledgeGraph)
|
|
|
|
|
kbByID.DELETE("/knowledge_graph", r.knowledgebaseHandler.DeleteKnowledgeGraph)
|
|
|
|
|
}
|
2026-03-04 19:17:16 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-26 11:54:10 +08:00
|
|
|
// Tenant routes (per-tenant resources)
|
2026-05-20 20:32:06 +08:00
|
|
|
tenant := v1.Group("/tenant")
|
2026-03-26 11:54:10 +08:00
|
|
|
{
|
2026-05-27 15:54:11 +08:00
|
|
|
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-03-26 11:54:10 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-21 18:10:00 +08:00
|
|
|
// Document routes
|
2026-05-20 20:32:06 +08:00
|
|
|
doc := v1.Group("/document")
|
2026-03-21 18:10:00 +08:00
|
|
|
{
|
|
|
|
|
doc.POST("/list", r.documentHandler.ListDocuments)
|
|
|
|
|
doc.POST("/metadata/summary", r.documentHandler.MetadataSummary)
|
2026-04-07 09:44:51 +08:00
|
|
|
doc.POST("/set_meta", r.documentHandler.SetMeta)
|
2026-05-25 19:15:07 +08:00
|
|
|
doc.POST("/delete_meta", r.documentHandler.DeleteMeta) // Internal API only for GO
|
2026-03-21 18:10:00 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-01 11:22:08 +08:00
|
|
|
v1.GET("/thumbnails", r.documentHandler.GetThumbnail)
|
|
|
|
|
|
2026-03-11 11:23:13 +08:00
|
|
|
// Chunk routes
|
2026-05-20 20:32:06 +08:00
|
|
|
chunk := v1.Group("/chunk")
|
2026-03-04 19:17:16 +08:00
|
|
|
{
|
2026-03-24 20:10:21 +08:00
|
|
|
chunk.POST("/list", r.chunkHandler.List)
|
2026-04-09 09:52:31 +08:00
|
|
|
chunk.POST("/update", r.chunkHandler.UpdateChunk) // Internal API only for GO
|
2026-03-04 19:17:16 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-11 11:23:13 +08:00
|
|
|
// Chat routes
|
|
|
|
|
chat := authorized.Group("/v1/dialog")
|
|
|
|
|
{
|
|
|
|
|
chat.POST("/next", r.chatHandler.ListChatsNext)
|
|
|
|
|
chat.POST("/set", r.chatHandler.SetDialog)
|
|
|
|
|
chat.POST("/rm", r.chatHandler.RemoveChats)
|
|
|
|
|
}
|
2026-03-04 19:17:16 +08:00
|
|
|
|
2026-03-11 11:23:13 +08:00
|
|
|
// Chat session (conversation) routes
|
|
|
|
|
session := authorized.Group("/v1/conversation")
|
|
|
|
|
{
|
|
|
|
|
session.POST("/set", r.chatSessionHandler.SetChatSession)
|
|
|
|
|
session.POST("/rm", r.chatSessionHandler.RemoveChatSessions)
|
|
|
|
|
session.GET("/list", r.chatSessionHandler.ListChatSessions)
|
|
|
|
|
session.POST("/completion", r.chatSessionHandler.Completion)
|
|
|
|
|
}
|
2026-03-04 19:17:16 +08:00
|
|
|
|
2026-03-11 11:23:13 +08:00
|
|
|
// Connector routes
|
|
|
|
|
connector := authorized.Group("/v1/connector")
|
|
|
|
|
{
|
|
|
|
|
connector.GET("/list", r.connectorHandler.ListConnectors)
|
2026-05-28 16:44:35 +08:00
|
|
|
connector.GET("/:connector_id", r.connectorHandler.GetConnector)
|
|
|
|
|
connector.POST("/:connector_id/rebuild", r.connectorHandler.RebuildConnector)
|
2026-03-11 11:23:13 +08:00
|
|
|
}
|
2026-03-04 19:17:16 +08:00
|
|
|
|
2026-03-11 11:23:13 +08:00
|
|
|
// 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-03-27 09:49:50 +08:00
|
|
|
|
2026-03-04 19:17:16 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-05 21:16:25 +08:00
|
|
|
// Dify retrieval routes
|
|
|
|
|
dify := authorized.Group("/api/v1/dify")
|
|
|
|
|
{
|
|
|
|
|
dify.POST("/retrieval", r.difyRetrievalHandler.Retrieval)
|
|
|
|
|
dify.GET("/retrieval", r.difyRetrievalHandler.Retrieval)
|
|
|
|
|
}
|
|
|
|
|
apiNoAuth.GET("/dify/retrieval/health", r.difyRetrievalHandler.HealthCheck)
|
|
|
|
|
|
2026-03-04 19:17:16 +08:00
|
|
|
// Handle undefined routes
|
|
|
|
|
engine.NoRoute(handler.HandleNoRoute)
|
|
|
|
|
}
|