WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,282 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ErrorCode defines the error code type
|
||||
type ErrorCode int
|
||||
|
||||
// System error codes
|
||||
const (
|
||||
// Common error codes (1000-1999)
|
||||
ErrBadRequest ErrorCode = 1000
|
||||
ErrUnauthorized ErrorCode = 1001
|
||||
ErrForbidden ErrorCode = 1002
|
||||
ErrNotFound ErrorCode = 1003
|
||||
ErrMethodNotAllowed ErrorCode = 1004
|
||||
ErrConflict ErrorCode = 1005
|
||||
ErrTooManyRequests ErrorCode = 1006
|
||||
ErrInternalServer ErrorCode = 1007
|
||||
ErrServiceUnavailable ErrorCode = 1008
|
||||
ErrTimeout ErrorCode = 1009
|
||||
ErrValidation ErrorCode = 1010
|
||||
|
||||
// Tenant related error codes (2000-2099)
|
||||
ErrTenantNotFound ErrorCode = 2000
|
||||
ErrTenantAlreadyExists ErrorCode = 2001
|
||||
ErrTenantInactive ErrorCode = 2002
|
||||
ErrTenantNameRequired ErrorCode = 2003
|
||||
ErrTenantInvalidStatus ErrorCode = 2004
|
||||
ErrTenantCreationDisabled ErrorCode = 2005
|
||||
|
||||
// Agent related error codes (2100-2199)
|
||||
ErrAgentMissingThinkingModel ErrorCode = 2100
|
||||
ErrAgentMissingAllowedTools ErrorCode = 2101
|
||||
ErrAgentInvalidMaxIterations ErrorCode = 2102
|
||||
ErrAgentInvalidTemperature ErrorCode = 2103
|
||||
|
||||
// VectorStore binding related error codes (2200-2299).
|
||||
// Both map to HTTP 400 with a generic message; the typed code lets
|
||||
// clients distinguish "wrong UUID / cross-tenant" from "store exists
|
||||
// but is currently unavailable" without parsing the message text.
|
||||
ErrVectorStoreBindingInvalid ErrorCode = 2200
|
||||
ErrVectorStoreUnavailable ErrorCode = 2201
|
||||
|
||||
// Model lifecycle errors (2300-2399).
|
||||
// The dedicated code lets clients render structured usage details without
|
||||
// parsing the backwards-compatible English error message.
|
||||
ErrModelInUse ErrorCode = 2300
|
||||
|
||||
// Add more error codes here
|
||||
)
|
||||
|
||||
// AppError defines the application error structure
|
||||
type AppError struct {
|
||||
Code ErrorCode `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Details any `json:"details,omitempty"`
|
||||
HTTPCode int `json:"-"`
|
||||
}
|
||||
|
||||
// Error implements the error interface
|
||||
func (e *AppError) Error() string {
|
||||
return fmt.Sprintf("error code: %d, error message: %s", e.Code, e.Message)
|
||||
}
|
||||
|
||||
// WithDetails adds error details
|
||||
func (e *AppError) WithDetails(details any) *AppError {
|
||||
e.Details = details
|
||||
return e
|
||||
}
|
||||
|
||||
// NewBadRequestError creates a bad request error
|
||||
func NewBadRequestError(message string) *AppError {
|
||||
return &AppError{
|
||||
Code: ErrBadRequest,
|
||||
Message: message,
|
||||
HTTPCode: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// NewModelInUseError creates the typed HTTP 400 returned when active tenant
|
||||
// resources still reference a model.
|
||||
func NewModelInUseError(message string, details any) *AppError {
|
||||
return &AppError{
|
||||
Code: ErrModelInUse,
|
||||
Message: message,
|
||||
Details: details,
|
||||
HTTPCode: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// NewUnauthorizedError creates an unauthorized error
|
||||
func NewUnauthorizedError(message string) *AppError {
|
||||
return &AppError{
|
||||
Code: ErrUnauthorized,
|
||||
Message: message,
|
||||
HTTPCode: http.StatusUnauthorized,
|
||||
}
|
||||
}
|
||||
|
||||
// NewForbiddenError creates a forbidden error
|
||||
func NewForbiddenError(message string) *AppError {
|
||||
return &AppError{
|
||||
Code: ErrForbidden,
|
||||
Message: message,
|
||||
HTTPCode: http.StatusForbidden,
|
||||
}
|
||||
}
|
||||
|
||||
// NewNotFoundError creates a not found error
|
||||
func NewNotFoundError(message string) *AppError {
|
||||
return &AppError{
|
||||
Code: ErrNotFound,
|
||||
Message: message,
|
||||
HTTPCode: http.StatusNotFound,
|
||||
}
|
||||
}
|
||||
|
||||
// NewConflictError creates a conflict error
|
||||
func NewConflictError(message string) *AppError {
|
||||
return &AppError{
|
||||
Code: ErrConflict,
|
||||
Message: message,
|
||||
HTTPCode: http.StatusConflict,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTooManyRequestsError creates a 429 error, used by quota-style
|
||||
// guards (e.g. per-user self-service tenant creation cap).
|
||||
func NewTooManyRequestsError(message string) *AppError {
|
||||
if message == "" {
|
||||
message = "too many requests"
|
||||
}
|
||||
return &AppError{
|
||||
Code: ErrTooManyRequests,
|
||||
Message: message,
|
||||
HTTPCode: http.StatusTooManyRequests,
|
||||
}
|
||||
}
|
||||
|
||||
// NewInternalServerError creates an internal server error
|
||||
func NewInternalServerError(message string) *AppError {
|
||||
if message == "" {
|
||||
message = "服务器内部错误"
|
||||
}
|
||||
return &AppError{
|
||||
Code: ErrInternalServer,
|
||||
Message: message,
|
||||
HTTPCode: http.StatusInternalServerError,
|
||||
}
|
||||
}
|
||||
|
||||
// NewServiceUnavailableError creates a service unavailable (503)
|
||||
// error. Used for transient failures where the caller can retry.
|
||||
func NewServiceUnavailableError(message string) *AppError {
|
||||
if message == "" {
|
||||
message = "服务暂时不可用"
|
||||
}
|
||||
return &AppError{
|
||||
Code: ErrServiceUnavailable,
|
||||
Message: message,
|
||||
HTTPCode: http.StatusServiceUnavailable,
|
||||
}
|
||||
}
|
||||
|
||||
// NewValidationError creates a validation error
|
||||
func NewValidationError(message string) *AppError {
|
||||
return &AppError{
|
||||
Code: ErrValidation,
|
||||
Message: message,
|
||||
HTTPCode: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// Tenant related errors
|
||||
func NewTenantNotFoundError() *AppError {
|
||||
return &AppError{
|
||||
Code: ErrTenantNotFound,
|
||||
Message: "空间不存在",
|
||||
HTTPCode: http.StatusNotFound,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTenantAlreadyExistsError creates a tenant already exists error
|
||||
func NewTenantAlreadyExistsError() *AppError {
|
||||
return &AppError{
|
||||
Code: ErrTenantAlreadyExists,
|
||||
Message: "空间已存在",
|
||||
HTTPCode: http.StatusConflict,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTenantInactiveError creates a tenant inactive error
|
||||
func NewTenantInactiveError() *AppError {
|
||||
return &AppError{
|
||||
Code: ErrTenantInactive,
|
||||
Message: "空间已停用",
|
||||
HTTPCode: http.StatusForbidden,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTenantCreationDisabledError reports a deployment-policy denial for
|
||||
// ordinary self-service tenant creation.
|
||||
func NewTenantCreationDisabledError() *AppError {
|
||||
return &AppError{
|
||||
Code: ErrTenantCreationDisabled,
|
||||
Message: "self-service workspace creation is disabled; join a workspace by invitation",
|
||||
HTTPCode: http.StatusForbidden,
|
||||
}
|
||||
}
|
||||
|
||||
// Agent related errors
|
||||
func NewAgentMissingThinkingModelError() *AppError {
|
||||
return &AppError{
|
||||
Code: ErrAgentMissingThinkingModel,
|
||||
Message: "启用Agent模式前,请先选择思考模型",
|
||||
HTTPCode: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
func NewAgentMissingAllowedToolsError() *AppError {
|
||||
return &AppError{
|
||||
Code: ErrAgentMissingAllowedTools,
|
||||
Message: "至少需要选择一个允许的工具",
|
||||
HTTPCode: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
func NewAgentInvalidMaxIterationsError() *AppError {
|
||||
return &AppError{
|
||||
Code: ErrAgentInvalidMaxIterations,
|
||||
Message: "最大迭代次数须为正整数,或 -1 表示不限制",
|
||||
HTTPCode: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
func NewAgentInvalidTemperatureError() *AppError {
|
||||
return &AppError{
|
||||
Code: ErrAgentInvalidTemperature,
|
||||
Message: "温度参数必须在0-2之间",
|
||||
HTTPCode: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// NewVectorStoreBindingInvalidError signals that a knowledge base create
|
||||
// request referenced a vector store that does not exist under the caller's
|
||||
// tenant (or carried a malformed UUID). The user-facing message is
|
||||
// intentionally generic to avoid enumeration oracles — see the structured
|
||||
// log at the call site for the tenant/store pair.
|
||||
func NewVectorStoreBindingInvalidError(message string) *AppError {
|
||||
if message == "" {
|
||||
message = "vector store not found"
|
||||
}
|
||||
return &AppError{
|
||||
Code: ErrVectorStoreBindingInvalid,
|
||||
Message: message,
|
||||
HTTPCode: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// NewVectorStoreUnavailableError signals that the requested vector store
|
||||
// row exists in the database but is not currently wired into the in-memory
|
||||
// engine registry (factory failure on CreateStore, dynamic config error,
|
||||
// or stale state after a connection-config rotation).
|
||||
func NewVectorStoreUnavailableError(message string) *AppError {
|
||||
if message == "" {
|
||||
message = "vector store is currently unavailable"
|
||||
}
|
||||
return &AppError{
|
||||
Code: ErrVectorStoreUnavailable,
|
||||
Message: message,
|
||||
HTTPCode: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// IsAppError checks if the error is an AppError type
|
||||
func IsAppError(err error) (*AppError, bool) {
|
||||
appErr, ok := err.(*AppError)
|
||||
return appErr, ok
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// Package errors: parse stage error codes.
|
||||
//
|
||||
// These constants are the stable wire format the frontend uses to map a
|
||||
// failure to a localized "what to do about it" message. Adding a new code
|
||||
// is non-breaking; renaming an existing one requires a coordinated
|
||||
// frontend release because the i18n keys are looked up by code.
|
||||
//
|
||||
// Codes are SCREAMING_SNAKE so they survive JSON case transforms and look
|
||||
// distinct from Go identifiers in logs/dashboards.
|
||||
package errors
|
||||
|
||||
const (
|
||||
// ErrCodeDocReaderTimeout — DocReader RPC exceeded
|
||||
// WEKNORA_DOCREADER_CALL_TIMEOUT (default 30m). Suggest splitting
|
||||
// large files or checking docreader load.
|
||||
ErrCodeDocReaderTimeout = "DOCREADER_TIMEOUT"
|
||||
|
||||
// ErrCodeDocReaderUnavailable — no DocReader configured for the
|
||||
// requested file type / engine, or the service refused connection.
|
||||
ErrCodeDocReaderUnavailable = "DOCREADER_UNAVAILABLE"
|
||||
|
||||
// ErrCodeDocReaderParseFailed — DocReader returned an explicit parse
|
||||
// error (encoding, corrupted file, OCR engine crash, ...).
|
||||
ErrCodeDocReaderParseFailed = "DOCREADER_PARSE_FAILED"
|
||||
|
||||
// ErrCodeChunkingFailed — text chunking step itself failed (rare;
|
||||
// usually only on extreme-size inputs).
|
||||
ErrCodeChunkingFailed = "CHUNKING_FAILED"
|
||||
|
||||
// ErrCodeEmbeddingRateLimit — embedding provider returned 429 or
|
||||
// equivalent. Suggest retrying later or scaling out.
|
||||
ErrCodeEmbeddingRateLimit = "EMBEDDING_RATE_LIMIT"
|
||||
|
||||
// ErrCodeEmbeddingProviderFail — non-rate-limit embedding error
|
||||
// (auth, model not found, bad input). Usually permanent without
|
||||
// config changes.
|
||||
ErrCodeEmbeddingProviderFail = "EMBEDDING_PROVIDER_FAIL"
|
||||
|
||||
// ErrCodeVectorStoreWriteFailed — chunks embedded fine but the
|
||||
// vector DB write failed (quota, connectivity, schema mismatch).
|
||||
ErrCodeVectorStoreWriteFailed = "VECTORSTORE_WRITE_FAILED"
|
||||
|
||||
// ErrCodeMultimodalVLMFailed — a single image's OCR or caption call
|
||||
// failed. Note: per-image failures don't fail the whole parent;
|
||||
// see image_multimodal.go finalize-on-last-attempt.
|
||||
ErrCodeMultimodalVLMFailed = "MULTIMODAL_VLM_FAILED"
|
||||
|
||||
// ErrCodeMultimodalAllFailed — every image task hit dead-letter.
|
||||
// The parent still completes (caption / OCR is optional content),
|
||||
// but stage status is marked failed so the UI can warn.
|
||||
ErrCodeMultimodalAllFailed = "MULTIMODAL_ALL_FAILED"
|
||||
|
||||
// ErrCodeTaskTimeout — asynq retry budget exhausted. Used by the
|
||||
// dead-letter callback when promoting a task failure into a stage
|
||||
// failure. Distinct from DocReaderTimeout: this is the asynq-level
|
||||
// timeout (whole task), not the docreader-call-level timeout.
|
||||
ErrCodeTaskTimeout = "TASK_TIMEOUT"
|
||||
|
||||
// ErrCodeUnknown — fallback when a wrapped error doesn't classify.
|
||||
// The full message is still recorded in error_detail so operators
|
||||
// can debug; the UI shows a generic "see admin" hint.
|
||||
ErrCodeUnknown = "UNKNOWN"
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
package errors
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
// ErrSessionNotFound session not found error
|
||||
ErrSessionNotFound = errors.New("session not found")
|
||||
// ErrSessionExpired session expired error
|
||||
ErrSessionExpired = errors.New("session expired")
|
||||
// ErrSessionLimitExceeded session limit exceeded error
|
||||
ErrSessionLimitExceeded = errors.New("session limit exceeded")
|
||||
// ErrInvalidSessionID invalid session ID error
|
||||
ErrInvalidSessionID = errors.New("invalid session id")
|
||||
// ErrInvalidTenantID invalid tenant ID error
|
||||
ErrInvalidTenantID = errors.New("invalid tenant id")
|
||||
)
|
||||
Reference in New Issue
Block a user