chore: import upstream snapshot with attribution
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
package eval
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
mcpserver "github.com/mark3labs/mcp-go/server"
|
||||
"github.com/zzet/gortex/internal/graph"
|
||||
"github.com/zzet/gortex/internal/server"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Handler extends server.Handler with eval-specific endpoints.
|
||||
// It inherits every /v1/* route from the base handler and adds
|
||||
// POST /v1/augment for the eval augment pipeline.
|
||||
type Handler struct {
|
||||
*server.Handler
|
||||
}
|
||||
|
||||
// NewHandler creates an eval HTTP handler that dispatches to MCP tools.
|
||||
// It provides all server /v1/* endpoints plus POST /v1/augment.
|
||||
func NewHandler(mcpServer *mcpserver.MCPServer, g *graph.Graph, version string, logger *zap.Logger) *Handler {
|
||||
base := server.NewHandler(mcpServer, g, version, logger)
|
||||
h := &Handler{Handler: base}
|
||||
base.Mux().HandleFunc("POST /v1/augment", h.handleAugment)
|
||||
return h
|
||||
}
|
||||
|
||||
// --- /augment (eval-specific) ---
|
||||
|
||||
type augmentRequest struct {
|
||||
Pattern string `json:"pattern"`
|
||||
}
|
||||
|
||||
type augmentResponse struct {
|
||||
Pattern string `json:"pattern"`
|
||||
Symbols []augmentSymbol `json:"symbols"`
|
||||
}
|
||||
|
||||
type augmentSymbol struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
File string `json:"file"`
|
||||
Kind string `json:"kind"`
|
||||
Callers []string `json:"callers,omitempty"`
|
||||
Callees []string `json:"callees,omitempty"`
|
||||
CallChain []string `json:"call_chain,omitempty"`
|
||||
}
|
||||
|
||||
func (h *Handler) handleAugment(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(io.LimitReader(r.Body, 1<<20))
|
||||
if err != nil {
|
||||
server.WriteJSONError(w, http.StatusBadRequest, "failed to read request body")
|
||||
return
|
||||
}
|
||||
|
||||
var req augmentRequest
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
server.WriteJSONError(w, http.StatusBadRequest, "malformed JSON: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if req.Pattern == "" {
|
||||
server.WriteJSONError(w, http.StatusBadRequest, "missing 'pattern' field")
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
|
||||
searchResults := h.CallTool(ctx, "search_symbols", map[string]any{
|
||||
"query": req.Pattern,
|
||||
"compact": true,
|
||||
})
|
||||
|
||||
symbolIDs := extractSymbolIDs(searchResults)
|
||||
|
||||
var symbols []augmentSymbol
|
||||
for _, id := range symbolIDs {
|
||||
sym := augmentSymbol{ID: id}
|
||||
|
||||
if parts := strings.SplitN(id, "::", 2); len(parts) == 2 {
|
||||
sym.File = parts[0]
|
||||
sym.Name = parts[1]
|
||||
} else {
|
||||
sym.Name = id
|
||||
}
|
||||
|
||||
if node := h.Graph().GetNode(id); node != nil {
|
||||
sym.Kind = string(node.Kind)
|
||||
}
|
||||
|
||||
usageResults := h.CallTool(ctx, "find_usages", map[string]any{
|
||||
"id": id,
|
||||
"compact": true,
|
||||
})
|
||||
sym.Callers = extractLines(usageResults)
|
||||
|
||||
chainResults := h.CallTool(ctx, "get_call_chain", map[string]any{
|
||||
"function_id": id,
|
||||
"compact": true,
|
||||
"depth": float64(2),
|
||||
})
|
||||
sym.CallChain = extractLines(chainResults)
|
||||
|
||||
symbols = append(symbols, sym)
|
||||
}
|
||||
|
||||
resp := augmentResponse{
|
||||
Pattern: req.Pattern,
|
||||
Symbols: symbols,
|
||||
}
|
||||
server.WriteJSON(w, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// --- Helpers ---
|
||||
|
||||
// extractSymbolIDs parses symbol IDs from compact search_symbols output.
|
||||
func extractSymbolIDs(text string) []string {
|
||||
if text == "" {
|
||||
return nil
|
||||
}
|
||||
var ids []string
|
||||
seen := make(map[string]bool)
|
||||
for _, line := range strings.Split(text, "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) >= 3 {
|
||||
name := parts[1]
|
||||
fileLine := parts[2]
|
||||
file := fileLine
|
||||
if idx := strings.LastIndex(fileLine, ":"); idx > 0 {
|
||||
file = fileLine[:idx]
|
||||
}
|
||||
id := file + "::" + name
|
||||
if !seen[id] {
|
||||
seen[id] = true
|
||||
ids = append(ids, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
// extractLines splits text into non-empty trimmed lines.
|
||||
func extractLines(text string) []string {
|
||||
if text == "" {
|
||||
return nil
|
||||
}
|
||||
var lines []string
|
||||
for _, line := range strings.Split(text, "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if line != "" {
|
||||
lines = append(lines, line)
|
||||
}
|
||||
}
|
||||
return lines
|
||||
}
|
||||
Reference in New Issue
Block a user