后端: 1.阶段 6 memory 服务化 CP1-CP3 落地 - 新增 cmd/memory 独立进程入口,落地 services/memory dao/rpc/sv 与 memory zrpc pb - 将 memory.extract.requested outbox 消费与 memory worker 迁入 cmd/memory,单体 worker 不再消费 memory outbox - 新增 gateway/client/memory、shared/contracts/memory 和 shared/ports memory port - 将 /api/v1/memory/items* HTTP 管理面切到 memory zrpc,gateway 只保留鉴权、限流、幂等、参数绑定和响应透传 - 新增 memory Retrieve RPC,并将 agent 主链路 memory reader 切到 memory zrpc 读取 - 补充 agent memory RPC reader 适配器,保留注入侧 observer / metrics 观测能力 - 保留旧 backend/memory 核心实现作为迁移期复用与回退面,cmd/memory 内部继续复用既有 Module / ReadService 逻辑 - 补充 memory.rpc 示例配置,更新单体 outbox 发布边界与 memory handler 注释口径
95 lines
2.4 KiB
Go
95 lines
2.4 KiB
Go
package memory
|
||
|
||
import (
|
||
"errors"
|
||
"fmt"
|
||
"strings"
|
||
|
||
"github.com/LoveLosita/smartflow/backend/respond"
|
||
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
||
"google.golang.org/grpc/codes"
|
||
"google.golang.org/grpc/status"
|
||
)
|
||
|
||
// responseFromRPCError 负责把 memory 的 gRPC 错误反解回项目内错误。
|
||
//
|
||
// 职责边界:
|
||
// 1. 只在 gateway 边缘层使用;
|
||
// 2. 业务错误尽量恢复成 respond.Response,方便 API 层复用 DealWithError;
|
||
// 3. 服务不可用或未知内部错误包装成普通 error,避免误报成用户可修正的参数问题。
|
||
func responseFromRPCError(err error) error {
|
||
if err == nil {
|
||
return nil
|
||
}
|
||
|
||
st, ok := status.FromError(err)
|
||
if !ok {
|
||
return wrapRPCError(err)
|
||
}
|
||
if resp, ok := responseFromStatus(st); ok {
|
||
return resp
|
||
}
|
||
|
||
switch st.Code() {
|
||
case codes.Internal, codes.Unknown, codes.Unavailable, codes.DeadlineExceeded, codes.DataLoss, codes.Unimplemented:
|
||
msg := strings.TrimSpace(st.Message())
|
||
if msg == "" {
|
||
msg = "memory zrpc service internal error"
|
||
}
|
||
return wrapRPCError(errors.New(msg))
|
||
}
|
||
|
||
msg := strings.TrimSpace(st.Message())
|
||
if msg == "" {
|
||
msg = "memory zrpc service rejected request"
|
||
}
|
||
return respond.Response{Status: grpcCodeToRespondStatus(st.Code()), Info: msg}
|
||
}
|
||
|
||
func responseFromStatus(st *status.Status) (respond.Response, bool) {
|
||
if st == nil {
|
||
return respond.Response{}, false
|
||
}
|
||
for _, detail := range st.Details() {
|
||
info, ok := detail.(*errdetails.ErrorInfo)
|
||
if !ok {
|
||
continue
|
||
}
|
||
statusValue := strings.TrimSpace(info.Reason)
|
||
if statusValue == "" {
|
||
statusValue = grpcCodeToRespondStatus(st.Code())
|
||
}
|
||
message := strings.TrimSpace(st.Message())
|
||
if message == "" && info.Metadata != nil {
|
||
message = strings.TrimSpace(info.Metadata["info"])
|
||
}
|
||
if message == "" {
|
||
message = statusValue
|
||
}
|
||
return respond.Response{Status: statusValue, Info: message}, true
|
||
}
|
||
return respond.Response{}, false
|
||
}
|
||
|
||
func grpcCodeToRespondStatus(code codes.Code) string {
|
||
switch code {
|
||
case codes.Unauthenticated:
|
||
return respond.ErrUnauthorized.Status
|
||
case codes.InvalidArgument:
|
||
return respond.MissingParam.Status
|
||
case codes.NotFound:
|
||
return respond.MemoryItemNotFound.Status
|
||
case codes.Internal, codes.Unknown, codes.DataLoss:
|
||
return "500"
|
||
default:
|
||
return "400"
|
||
}
|
||
}
|
||
|
||
func wrapRPCError(err error) error {
|
||
if err == nil {
|
||
return nil
|
||
}
|
||
return fmt.Errorf("调用 memory zrpc 服务失败: %w", err)
|
||
}
|