Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions monkeyai/backend/api/admin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2108,6 +2108,101 @@ paths:
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalError'
/api/admin/v1/feedback:
get:
summary: 分页查询用户反馈
parameters:
- name: page
in: query
schema: {type: integer, minimum: 1, maximum: 1000000, default: 1}
- name: page_size
in: query
schema: {type: integer, minimum: 1, maximum: 100, default: 20}
- name: category
in: query
schema: {type: string, enum: [bug, feature, experience, other]}
- name: state
in: query
schema: {type: string, enum: [uploading, new, resolved, ignored, failed]}
responses:
'200':
description: 反馈分页
content:
application/json:
schema:
$ref: '#/components/schemas/FeedbackPage'
'400': {$ref: '#/components/responses/ResourceError400'}
'401': {$ref: '#/components/responses/ResourceError401'}
'403': {$ref: '#/components/responses/ResourceError403'}
'500': {$ref: '#/components/responses/ResourceError500'}
/api/admin/v1/feedback/{id}:
parameters:
- name: id
in: path
required: true
schema: {type: string, format: uuid}
get:
summary: 读取用户反馈详情
responses:
'200':
description: 反馈详情
content:
application/json:
schema:
$ref: '#/components/schemas/Feedback'
'401': {$ref: '#/components/responses/ResourceError401'}
'403': {$ref: '#/components/responses/ResourceError403'}
'404': {$ref: '#/components/responses/ResourceError404'}
'500': {$ref: '#/components/responses/ResourceError500'}
patch:
summary: 更新用户反馈状态
requestBody:
required: true
content:
application/json:
schema:
type: object
additionalProperties: false
required: [state]
properties:
state:
type: string
enum: [uploading, new, resolved, ignored, failed]
responses:
'200':
description: 更新后的反馈
content:
application/json:
schema:
$ref: '#/components/schemas/Feedback'
'400': {$ref: '#/components/responses/ResourceError400'}
'401': {$ref: '#/components/responses/ResourceError401'}
'403': {$ref: '#/components/responses/ResourceError403'}
'404': {$ref: '#/components/responses/ResourceError404'}
'500': {$ref: '#/components/responses/ResourceError500'}
/api/admin/v1/feedback/{id}/attachments/{attachmentID}:
get:
summary: 下载用户反馈图片
parameters:
- name: id
in: path
required: true
schema: {type: string, format: uuid}
- name: attachmentID
in: path
required: true
schema: {type: string, format: uuid}
responses:
'200':
description: 反馈图片
content:
image/png: {schema: {type: string, format: binary}}
image/jpeg: {schema: {type: string, format: binary}}
image/webp: {schema: {type: string, format: binary}}
'401': {$ref: '#/components/responses/ResourceError401'}
'403': {$ref: '#/components/responses/ResourceError403'}
'404': {$ref: '#/components/responses/ResourceError404'}
'500': {$ref: '#/components/responses/ResourceError500'}
/api/admin/v1/rules:
get:
summary: 查询Rule
Expand Down Expand Up @@ -4499,6 +4594,53 @@ components:
schema:
$ref: "#/components/schemas/ErrorResponse"
schemas:
FeedbackPage:
type: object
additionalProperties: false
required: [items, total, page, page_size]
properties:
items:
type: array
items:
$ref: '#/components/schemas/Feedback'
total: {type: integer, format: int64, minimum: 0}
page: {type: integer, minimum: 1}
page_size: {type: integer, minimum: 1}
Feedback:
type: object
additionalProperties: false
required: [id, user_id, category, content, platform, client_version, state, created_at, updated_at, attachments]
properties:
id: {type: string, format: uuid}
user_id: {type: string, format: uuid}
category: {type: string, enum: [bug, feature, experience, other]}
content: {type: string, maxLength: 5000}
rating: {type: [integer, 'null'], minimum: 1, maximum: 5}
platform: {type: string, enum: [desktop, mobile, web, other]}
client_version: {type: string, maxLength: 64}
state: {type: string, enum: [uploading, new, resolved, ignored, failed]}
request_id: {type: string}
created_at: {type: string, format: date-time}
updated_at: {type: string, format: date-time}
attachments:
type: array
items:
$ref: '#/components/schemas/FeedbackAttachment'
FeedbackAttachment:
type: object
additionalProperties: false
required: [id, feedback_id, mime_type, byte_size, width, height, sha256, state, downloadable, created_at]
properties:
id: {type: string, format: uuid}
feedback_id: {type: string, format: uuid}
mime_type: {type: string, enum: [image/png, image/jpeg, image/webp]}
byte_size: {type: integer, format: int64, minimum: 1}
width: {type: integer, minimum: 1}
height: {type: integer, minimum: 1}
sha256: {type: string, pattern: '^[0-9a-f]{64}$'}
state: {type: string, enum: [pending, ready, failed]}
downloadable: {type: boolean}
created_at: {type: string, format: date-time}
EmailAuthError:
type: object
required:
Expand Down
88 changes: 88 additions & 0 deletions monkeyai/backend/api/agent.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2792,6 +2792,41 @@ paths:
image/webp: {schema: {type: string, format: binary}}
'401': {$ref: '#/components/responses/Unauthorized'}
'404': {$ref: '#/components/responses/ResourceError404'}
/api/v1/feedback:
post:
summary: 提交用户反馈
description: >-
使用 Agent OAuth access token 提交文字反馈和可选截图。请求使用 multipart/form-data,最多上传 3 张
PNG、JPEG 或 WebP 图片,单张最多 5 MiB,总请求最多 16 MiB。content 和 images 至少提供一项。
parameters:
- name: Idempotency-Key
in: header
required: false
description: 建议客户端为可重试请求提供;同一用户相同 Key 和请求内容只创建一条反馈。
schema: {type: string, maxLength: 128}
requestBody:
required: true
content:
multipart/form-data:
schema:
$ref: '#/components/schemas/FeedbackMultipartInput'
responses:
'201':
description: 反馈已创建
content:
application/json:
schema:
$ref: '#/components/schemas/FeedbackSubmission'
'200':
description: 幂等键命中已有反馈
content:
application/json:
schema:
$ref: '#/components/schemas/FeedbackSubmission'
'400': {$ref: '#/components/responses/ResourceError400'}
'401': {$ref: '#/components/responses/Unauthorized'}
'409': {$ref: '#/components/responses/ResourceError409'}
'500': {$ref: '#/components/responses/ResourceError500'}
components:
securitySchemes:
ModelInvocationKey:
Expand Down Expand Up @@ -2992,6 +3027,59 @@ components:
schema:
$ref: "#/components/schemas/ErrorResponse"
schemas:
FeedbackMultipartInput:
type: object
additionalProperties: false
required: [category]
properties:
category:
type: string
enum: [bug, feature, experience, other]
content:
type: string
maxLength: 5000
rating:
type: integer
minimum: 1
maximum: 5
platform:
type: string
enum: [desktop, mobile, web, other]
default: other
client_version:
type: string
maxLength: 64
images:
type: array
maxItems: 3
items:
type: string
format: binary
description: PNG、JPEG 或 WebP 图片;单张最多 5 MiB。
FeedbackSubmission:
type: object
additionalProperties: false
required: [id, created_at, attachments]
properties:
id: {type: string, format: uuid}
created_at: {type: string, format: date-time}
attachments:
type: array
items:
$ref: '#/components/schemas/FeedbackAttachment'
FeedbackAttachment:
type: object
additionalProperties: false
required: [id, mime_type, byte_size, width, height, downloadable]
properties:
id: {type: string, format: uuid}
mime_type:
type: string
enum: [image/png, image/jpeg, image/webp]
byte_size: {type: integer, format: int64, minimum: 1}
width: {type: integer, minimum: 1}
height: {type: integer, minimum: 1}
downloadable: {type: boolean}
Endpoint:
type: object
required: [machine_id, device_name, alias, display_name, platform, os_version, arch, client_version, protocol_version, status, online, created_at, updated_at, last_seen_at, revoked_at]
Expand Down
19 changes: 17 additions & 2 deletions monkeyai/backend/internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/chaitin/MonkeyCode/monkeyai/backend/internal/database"
"github.com/chaitin/MonkeyCode/monkeyai/backend/internal/endpoint"
"github.com/chaitin/MonkeyCode/monkeyai/backend/internal/expert"
"github.com/chaitin/MonkeyCode/monkeyai/backend/internal/feedback"
"github.com/chaitin/MonkeyCode/monkeyai/backend/internal/group"
"github.com/chaitin/MonkeyCode/monkeyai/backend/internal/httpapi"
"github.com/chaitin/MonkeyCode/monkeyai/backend/internal/identity"
Expand Down Expand Up @@ -49,6 +50,7 @@ type App struct {
images *imagegen.Service
inputs *imagegen.Inputs
endpoints *endpoint.Service
feedback *feedback.Service
}

func New(ctx context.Context, cfg config.Config, logger *slog.Logger) (*App, error) {
Expand Down Expand Up @@ -86,6 +88,7 @@ func New(ctx context.Context, cfg config.Config, logger *slog.Logger) (*App, err
images: handler.(*applicationHandler).images,
inputs: handler.(*applicationHandler).inputs,
endpoints: handler.(*applicationHandler).endpoints,
feedback: handler.(*applicationHandler).feedback,
database: pool,
shutdownTimeout: cfg.ShutdownTimeout,
}, nil
Expand Down Expand Up @@ -127,6 +130,7 @@ func newApplicationHandler(ctx context.Context, logger *slog.Logger, pool *pgxpo
if err != nil {
return nil, fmt.Errorf("初始化资源 Bucket: %w", err)
}
feedbacks := feedback.NewService(pool, storage)
imageRepo := imagegen.NewPostgres(pool)
imageInputs := imagegen.NewInputs(imageRepo, storage)
imageOutputs := imagegen.NewOutputs(imageRepo, storage)
Expand Down Expand Up @@ -171,6 +175,7 @@ func newApplicationHandler(ctx context.Context, logger *slog.Logger, pool *pgxpo
connectors.RegisterAdmin(admin)
experts.RegisterAdmin(admin)
resources.RegisterAdmin(admin)
feedbacks.RegisterAdmin(admin)

agent := chi.NewRouter()
agent.Use(identities.RequireAgent)
Expand All @@ -187,6 +192,7 @@ func newApplicationHandler(ctx context.Context, logger *slog.Logger, pool *pgxpo
connectors.RegisterAgent(agent)
resources.RegisterAgent(agent)
charges.RegisterAgent(agent)
feedbacks.RegisterAgent(agent)

router := chi.NewRouter()
modelProxy := proxy.NewProxy(modelResolver{service: models}, logger).WithBilling(modelBilling{service: charges}).WithUsageRecorder(modelUsageRecorder{models: modelRepo})
Expand All @@ -211,7 +217,7 @@ func newApplicationHandler(ctx context.Context, logger *slog.Logger, pool *pgxpo
}
})(identities.AuthRouter())
router.Mount("/", httpapi.New(logger, readiness{pool: pool, storage: storage, endpoints: endpoints}, admin, agent, auth))
return &applicationHandler{Handler: router, billing: charges, proxy: modelProxy, images: imageService, inputs: imageInputs, endpoints: endpoints}, nil
return &applicationHandler{Handler: router, billing: charges, proxy: modelProxy, images: imageService, inputs: imageInputs, endpoints: endpoints, feedback: feedbacks}, nil
}

type modelResolver struct {
Expand Down Expand Up @@ -245,7 +251,16 @@ func (a *App) Run(ctx context.Context) error {
go func() { defer close(imageDone); a.images.Run(workerCtx) }()
cleanupDone := make(chan struct{})
go func() { defer close(cleanupDone); runImageCleanup(workerCtx, a.inputs) }()
defer func() { stopWorker(); <-workerDone; <-observeDone; <-imageDone; <-cleanupDone }()
feedbackCleanupDone := make(chan struct{})
go func() { defer close(feedbackCleanupDone); _ = a.feedback.Run(workerCtx, time.Hour) }()
defer func() {
stopWorker()
<-workerDone
<-observeDone
<-imageDone
<-cleanupDone
<-feedbackCleanupDone
}()

result := make(chan error, len(a.servers))
for _, server := range a.servers {
Expand Down
2 changes: 2 additions & 0 deletions monkeyai/backend/internal/app/billing.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/chaitin/MonkeyCode/monkeyai/backend/internal/billing"
"github.com/chaitin/MonkeyCode/monkeyai/backend/internal/endpoint"
"github.com/chaitin/MonkeyCode/monkeyai/backend/internal/feedback"
"github.com/chaitin/MonkeyCode/monkeyai/backend/internal/imagegen"
"github.com/chaitin/MonkeyCode/monkeyai/backend/internal/mcp"
"github.com/chaitin/MonkeyCode/monkeyai/backend/internal/model"
Expand All @@ -23,6 +24,7 @@ type applicationHandler struct {
images *imagegen.Service
inputs *imagegen.Inputs
endpoints *endpoint.Service
feedback *feedback.Service
}
type modelBilling struct{ service *billing.Service }
type modelUsageRecorder struct{ models *model.Postgres }
Expand Down
Loading
Loading