refactor api server around rpc services

This commit is contained in:
lpf
2026-03-15 01:00:41 +08:00
parent 341e578c9f
commit 231529e907
32 changed files with 5956 additions and 3614 deletions

63
pkg/rpc/admin.go Normal file
View File

@@ -0,0 +1,63 @@
package rpc
import "context"
type ConfigService interface {
View(context.Context, ConfigViewRequest) (*ConfigViewResponse, *Error)
Save(context.Context, ConfigSaveRequest) (*ConfigSaveResponse, *Error)
}
type CronService interface {
List(context.Context, ListCronJobsRequest) (*ListCronJobsResponse, *Error)
Get(context.Context, GetCronJobRequest) (*GetCronJobResponse, *Error)
Mutate(context.Context, MutateCronJobRequest) (*MutateCronJobResponse, *Error)
}
type ConfigViewRequest struct {
Mode string `json:"mode,omitempty"`
IncludeHotReloadInfo bool `json:"include_hot_reload_info,omitempty"`
}
type ConfigViewResponse struct {
Config interface{} `json:"config,omitempty"`
RawConfig interface{} `json:"raw_config,omitempty"`
PrettyText string `json:"pretty_text,omitempty"`
HotReloadFields []string `json:"hot_reload_fields,omitempty"`
HotReloadFieldDetails []map[string]interface{} `json:"hot_reload_field_details,omitempty"`
}
type ConfigSaveRequest struct {
Mode string `json:"mode,omitempty"`
ConfirmRisky bool `json:"confirm_risky,omitempty"`
Config map[string]interface{} `json:"config"`
}
type ConfigSaveResponse struct {
Saved bool `json:"saved"`
RequiresConfirm bool `json:"requires_confirm,omitempty"`
ChangedFields []string `json:"changed_fields,omitempty"`
Details interface{} `json:"details,omitempty"`
}
type ListCronJobsRequest struct{}
type ListCronJobsResponse struct {
Jobs []interface{} `json:"jobs"`
}
type GetCronJobRequest struct {
ID string `json:"id"`
}
type GetCronJobResponse struct {
Job interface{} `json:"job,omitempty"`
}
type MutateCronJobRequest struct {
Action string `json:"action"`
Args map[string]interface{} `json:"args,omitempty"`
}
type MutateCronJobResponse struct {
Result interface{} `json:"result,omitempty"`
}

37
pkg/rpc/envelope.go Normal file
View File

@@ -0,0 +1,37 @@
package rpc
import (
"encoding/json"
"strings"
)
type Request struct {
Method string `json:"method"`
Params json.RawMessage `json:"params,omitempty"`
RequestID string `json:"request_id,omitempty"`
}
type Response struct {
OK bool `json:"ok"`
Result interface{} `json:"result,omitempty"`
Error *Error `json:"error,omitempty"`
RequestID string `json:"request_id,omitempty"`
}
type Error struct {
Code string `json:"code"`
Message string `json:"message"`
Details interface{} `json:"details,omitempty"`
Retryable bool `json:"retryable,omitempty"`
}
func (r Request) NormalizedMethod() string {
return strings.ToLower(strings.TrimSpace(r.Method))
}
func DecodeParams(raw json.RawMessage, target interface{}) error {
if len(raw) == 0 || string(raw) == "null" {
return nil
}
return json.Unmarshal(raw, target)
}

192
pkg/rpc/node.go Normal file
View File

@@ -0,0 +1,192 @@
package rpc
import (
"context"
"github.com/YspCoder/clawgo/pkg/nodes"
)
type NodeService interface {
Register(context.Context, RegisterNodeRequest) (*RegisterNodeResponse, *Error)
Heartbeat(context.Context, HeartbeatNodeRequest) (*HeartbeatNodeResponse, *Error)
Dispatch(context.Context, DispatchNodeRequest) (*DispatchNodeResponse, *Error)
ListArtifacts(context.Context, ListNodeArtifactsRequest) (*ListNodeArtifactsResponse, *Error)
GetArtifact(context.Context, GetNodeArtifactRequest) (*GetNodeArtifactResponse, *Error)
DeleteArtifact(context.Context, DeleteNodeArtifactRequest) (*DeleteNodeArtifactResponse, *Error)
PruneArtifacts(context.Context, PruneNodeArtifactsRequest) (*PruneNodeArtifactsResponse, *Error)
}
type RegisterNodeRequest struct {
Node nodes.NodeInfo `json:"node"`
}
type RegisterNodeResponse struct {
ID string `json:"id"`
}
type HeartbeatNodeRequest struct {
ID string `json:"id"`
}
type HeartbeatNodeResponse struct {
ID string `json:"id"`
}
type DispatchNodeRequest struct {
Node string `json:"node"`
Action string `json:"action"`
Mode string `json:"mode,omitempty"`
Task string `json:"task,omitempty"`
Model string `json:"model,omitempty"`
Args map[string]interface{} `json:"args,omitempty"`
}
type DispatchNodeResponse struct {
Result nodes.Response `json:"result"`
}
type ArtifactSummary struct {
Data map[string]interface{} `json:"data"`
}
type ArtifactContentRef struct {
Data map[string]interface{} `json:"data"`
}
type ArtifactDeleteResult struct {
ID string `json:"id"`
DeletedFile bool `json:"deleted_file"`
DeletedAudit bool `json:"deleted_audit"`
}
type ArtifactPruneResult struct {
Pruned int `json:"pruned"`
DeletedFiles int `json:"deleted_files"`
Kept int `json:"kept"`
}
type ListNodeArtifactsRequest struct {
Node string `json:"node,omitempty"`
Action string `json:"action,omitempty"`
Kind string `json:"kind,omitempty"`
Limit int `json:"limit,omitempty"`
}
type ListNodeArtifactsResponse struct {
Items []map[string]interface{} `json:"items"`
ArtifactRetention map[string]interface{} `json:"artifact_retention,omitempty"`
}
type GetNodeArtifactRequest struct {
ID string `json:"id"`
}
type GetNodeArtifactResponse struct {
Found bool `json:"found"`
Artifact map[string]interface{} `json:"artifact,omitempty"`
}
type DeleteNodeArtifactRequest struct {
ID string `json:"id"`
}
type DeleteNodeArtifactResponse struct {
ArtifactDeleteResult
}
type PruneNodeArtifactsRequest struct {
Node string `json:"node,omitempty"`
Action string `json:"action,omitempty"`
Kind string `json:"kind,omitempty"`
KeepLatest int `json:"keep_latest,omitempty"`
Limit int `json:"limit,omitempty"`
}
type PruneNodeArtifactsResponse struct {
ArtifactPruneResult
}
type ProviderService interface {
ListModels(context.Context, ListProviderModelsRequest) (*ListProviderModelsResponse, *Error)
UpdateModels(context.Context, UpdateProviderModelsRequest) (*UpdateProviderModelsResponse, *Error)
Chat(context.Context, ProviderChatRequest) (*ProviderChatResponse, *Error)
CountTokens(context.Context, ProviderCountTokensRequest) (*ProviderCountTokensResponse, *Error)
RuntimeView(context.Context, ProviderRuntimeViewRequest) (*ProviderRuntimeViewResponse, *Error)
RuntimeAction(context.Context, ProviderRuntimeActionRequest) (*ProviderRuntimeActionResponse, *Error)
}
type ListProviderModelsRequest struct {
Provider string `json:"provider"`
}
type ListProviderModelsResponse struct {
Provider string `json:"provider"`
Models []string `json:"models,omitempty"`
Default string `json:"default_model,omitempty"`
}
type UpdateProviderModelsRequest struct {
Provider string `json:"provider"`
Model string `json:"model,omitempty"`
Models []string `json:"models,omitempty"`
}
type UpdateProviderModelsResponse struct {
Provider string `json:"provider"`
Models []string `json:"models,omitempty"`
}
type ProviderChatRequest struct {
Provider string `json:"provider"`
Model string `json:"model,omitempty"`
Messages []map[string]interface{} `json:"messages"`
Tools []map[string]interface{} `json:"tools,omitempty"`
Options map[string]interface{} `json:"options,omitempty"`
}
type ProviderChatResponse struct {
Content string `json:"content,omitempty"`
ToolCalls []map[string]interface{} `json:"tool_calls,omitempty"`
FinishReason string `json:"finish_reason,omitempty"`
Usage map[string]interface{} `json:"usage,omitempty"`
}
type ProviderCountTokensRequest struct {
Provider string `json:"provider"`
Model string `json:"model,omitempty"`
Messages []map[string]interface{} `json:"messages"`
Tools []map[string]interface{} `json:"tools,omitempty"`
Options map[string]interface{} `json:"options,omitempty"`
}
type ProviderCountTokensResponse struct {
Usage map[string]interface{} `json:"usage,omitempty"`
}
type ProviderRuntimeViewRequest struct {
Provider string `json:"provider,omitempty"`
Kind string `json:"kind,omitempty"`
Reason string `json:"reason,omitempty"`
Target string `json:"target,omitempty"`
Sort string `json:"sort,omitempty"`
ChangesOnly bool `json:"changes_only,omitempty"`
WindowSec int `json:"window_sec,omitempty"`
Limit int `json:"limit,omitempty"`
Cursor int `json:"cursor,omitempty"`
HealthBelow int `json:"health_below,omitempty"`
CooldownUntilBeforeSec int `json:"cooldown_until_before_sec,omitempty"`
}
type ProviderRuntimeViewResponse struct {
View map[string]interface{} `json:"view"`
}
type ProviderRuntimeActionRequest struct {
Provider string `json:"provider,omitempty"`
Action string `json:"action"`
OnlyExpiring bool `json:"only_expiring,omitempty"`
}
type ProviderRuntimeActionResponse struct {
Result map[string]interface{} `json:"result"`
}

56
pkg/rpc/registry.go Normal file
View File

@@ -0,0 +1,56 @@
package rpc
import (
"context"
"encoding/json"
"strings"
)
type MethodHandler func(context.Context, json.RawMessage) (interface{}, *Error)
type Registry struct {
methods map[string]MethodHandler
}
func NewRegistry() *Registry {
return &Registry{methods: map[string]MethodHandler{}}
}
func (r *Registry) Register(method string, handler MethodHandler) {
if r == nil || handler == nil {
return
}
method = strings.ToLower(strings.TrimSpace(method))
if method == "" {
return
}
r.methods[method] = handler
}
func (r *Registry) Handle(ctx context.Context, req Request) (interface{}, *Error) {
if r == nil {
return nil, &Error{Code: "internal", Message: "rpc registry unavailable"}
}
handler := r.methods[req.NormalizedMethod()]
if handler == nil {
return nil, &Error{
Code: "invalid_argument",
Message: "unknown method",
Details: map[string]interface{}{"method": strings.TrimSpace(req.Method)},
}
}
return handler(ctx, req.Params)
}
func RegisterJSON[T any](r *Registry, method string, handler func(context.Context, T) (interface{}, *Error)) {
if r == nil || handler == nil {
return
}
r.Register(method, func(ctx context.Context, raw json.RawMessage) (interface{}, *Error) {
var params T
if err := DecodeParams(raw, &params); err != nil {
return nil, &Error{Code: "invalid_argument", Message: err.Error()}
}
return handler(ctx, params)
})
}

87
pkg/rpc/subagent.go Normal file
View File

@@ -0,0 +1,87 @@
package rpc
import (
"context"
"github.com/YspCoder/clawgo/pkg/tools"
)
type SubagentService interface {
List(context.Context, ListSubagentsRequest) (*ListSubagentsResponse, *Error)
Snapshot(context.Context, SnapshotRequest) (*SnapshotResponse, *Error)
Get(context.Context, GetSubagentRequest) (*GetSubagentResponse, *Error)
Spawn(context.Context, SpawnSubagentRequest) (*SpawnSubagentResponse, *Error)
DispatchAndWait(context.Context, DispatchAndWaitRequest) (*DispatchAndWaitResponse, *Error)
Registry(context.Context, RegistryRequest) (*RegistryResponse, *Error)
}
type ListSubagentsRequest struct{}
type ListSubagentsResponse struct {
Items []*tools.SubagentTask `json:"items"`
}
type SnapshotRequest struct {
Limit int `json:"limit,omitempty"`
}
type SnapshotResponse struct {
Snapshot tools.RuntimeSnapshot `json:"snapshot"`
}
type GetSubagentRequest struct {
ID string `json:"id"`
}
type GetSubagentResponse struct {
Found bool `json:"found"`
Task *tools.SubagentTask `json:"task,omitempty"`
}
type SpawnSubagentRequest struct {
Task string `json:"task"`
Label string `json:"label,omitempty"`
Role string `json:"role,omitempty"`
AgentID string `json:"agent_id,omitempty"`
MaxRetries int `json:"max_retries,omitempty"`
RetryBackoffMS int `json:"retry_backoff_ms,omitempty"`
TimeoutSec int `json:"timeout_sec,omitempty"`
MaxTaskChars int `json:"max_task_chars,omitempty"`
MaxResultChars int `json:"max_result_chars,omitempty"`
Channel string `json:"channel,omitempty"`
ChatID string `json:"chat_id,omitempty"`
}
type SpawnSubagentResponse struct {
Message string `json:"message"`
}
type DispatchAndWaitRequest struct {
Task string `json:"task"`
Label string `json:"label,omitempty"`
Role string `json:"role,omitempty"`
AgentID string `json:"agent_id,omitempty"`
ThreadID string `json:"thread_id,omitempty"`
CorrelationID string `json:"correlation_id,omitempty"`
ParentRunID string `json:"parent_run_id,omitempty"`
Channel string `json:"channel,omitempty"`
ChatID string `json:"chat_id,omitempty"`
MaxRetries int `json:"max_retries,omitempty"`
RetryBackoffMS int `json:"retry_backoff_ms,omitempty"`
TimeoutSec int `json:"timeout_sec,omitempty"`
MaxTaskChars int `json:"max_task_chars,omitempty"`
MaxResultChars int `json:"max_result_chars,omitempty"`
WaitTimeoutSec int `json:"wait_timeout_sec,omitempty"`
}
type DispatchAndWaitResponse struct {
Task *tools.SubagentTask `json:"task,omitempty"`
Reply *tools.RouterReply `json:"reply,omitempty"`
Merged string `json:"merged,omitempty"`
}
type RegistryRequest struct{}
type RegistryResponse struct {
Items []map[string]interface{} `json:"items"`
}

50
pkg/rpc/workspace.go Normal file
View File

@@ -0,0 +1,50 @@
package rpc
import "context"
type WorkspaceService interface {
ListFiles(context.Context, ListWorkspaceFilesRequest) (*ListWorkspaceFilesResponse, *Error)
ReadFile(context.Context, ReadWorkspaceFileRequest) (*ReadWorkspaceFileResponse, *Error)
WriteFile(context.Context, WriteWorkspaceFileRequest) (*WriteWorkspaceFileResponse, *Error)
DeleteFile(context.Context, DeleteWorkspaceFileRequest) (*DeleteWorkspaceFileResponse, *Error)
}
type ListWorkspaceFilesRequest struct {
Scope string `json:"scope,omitempty"`
}
type ListWorkspaceFilesResponse struct {
Files []string `json:"files"`
}
type ReadWorkspaceFileRequest struct {
Scope string `json:"scope,omitempty"`
Path string `json:"path"`
}
type ReadWorkspaceFileResponse struct {
Path string `json:"path,omitempty"`
Found bool `json:"found,omitempty"`
Content string `json:"content,omitempty"`
}
type WriteWorkspaceFileRequest struct {
Scope string `json:"scope,omitempty"`
Path string `json:"path"`
Content string `json:"content"`
}
type WriteWorkspaceFileResponse struct {
Path string `json:"path,omitempty"`
Saved bool `json:"saved,omitempty"`
}
type DeleteWorkspaceFileRequest struct {
Scope string `json:"scope,omitempty"`
Path string `json:"path"`
}
type DeleteWorkspaceFileResponse struct {
Path string `json:"path,omitempty"`
Deleted bool `json:"deleted"`
}