102 lines
2.9 KiB
Go
102 lines
2.9 KiB
Go
|
|
package skill
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"fmt"
|
||
|
|
"strings"
|
||
|
|
)
|
||
|
|
|
||
|
|
// ManualInput is the form payload for the "manual" source type: the operator
|
||
|
|
// types one tool's details directly. Everything the synthesis pass needs is
|
||
|
|
// here, so manual skills can be produced without any LLM call.
|
||
|
|
type ManualInput struct {
|
||
|
|
Name string `json:"name"`
|
||
|
|
Description string `json:"description"`
|
||
|
|
Method string `json:"method"`
|
||
|
|
URL string `json:"url"`
|
||
|
|
QueryParams map[string]string `json:"query_params"` // name -> placeholder target; we store as {{name}}
|
||
|
|
PathParams []string `json:"path_params"` // names appearing in URL as {name}
|
||
|
|
Headers map[string]string `json:"headers"`
|
||
|
|
// ParamSchema, when supplied, overrides the flat param construction below.
|
||
|
|
ParamSchema json.RawMessage `json:"param_schema"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// ManualCandidate builds a CandidateTool from a manual form submission. It is
|
||
|
|
// the simplest importer: the operator has already done the LLM's job by
|
||
|
|
// hand, so we just normalise into our shapes.
|
||
|
|
func ManualCandidate(in ManualInput) (CandidateTool, error) {
|
||
|
|
name := strings.TrimSpace(in.Name)
|
||
|
|
if name == "" {
|
||
|
|
return CandidateTool{}, fmt.Errorf("工具名称必填")
|
||
|
|
}
|
||
|
|
if strings.TrimSpace(in.URL) == "" {
|
||
|
|
return CandidateTool{}, fmt.Errorf("URL 必填")
|
||
|
|
}
|
||
|
|
method := strings.ToUpper(strings.TrimSpace(in.Method))
|
||
|
|
if method == "" {
|
||
|
|
method = "GET"
|
||
|
|
}
|
||
|
|
|
||
|
|
// Default param schema: every query and path param is a required string.
|
||
|
|
props := map[string]any{}
|
||
|
|
var required []string
|
||
|
|
for _, n := range in.PathParams {
|
||
|
|
n = strings.TrimSpace(n)
|
||
|
|
if n == "" {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
props[n] = map[string]any{"type": "string"}
|
||
|
|
required = append(required, n)
|
||
|
|
}
|
||
|
|
query := map[string]string{}
|
||
|
|
for n := range in.QueryParams {
|
||
|
|
n = strings.TrimSpace(n)
|
||
|
|
if n == "" {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
props[n] = map[string]any{"type": "string"}
|
||
|
|
query[n] = "{{" + n + "}}"
|
||
|
|
}
|
||
|
|
for n := range in.Headers {
|
||
|
|
n = strings.TrimSpace(n)
|
||
|
|
if n == "" {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
props[n] = map[string]any{"type": "string"}
|
||
|
|
}
|
||
|
|
|
||
|
|
var paramsJSON jsonObject
|
||
|
|
if len(in.ParamSchema) > 0 && strings.TrimSpace(string(in.ParamSchema)) != "" {
|
||
|
|
paramsJSON = jsonObject(in.ParamSchema)
|
||
|
|
} else {
|
||
|
|
schema := map[string]any{"type": "object", "properties": props}
|
||
|
|
if len(required) > 0 {
|
||
|
|
schema["required"] = required
|
||
|
|
}
|
||
|
|
b, _ := json.Marshal(schema)
|
||
|
|
paramsJSON = jsonObject(b)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Path params map: stored so the executor can template {name} in the URL.
|
||
|
|
pathParams := map[string]string{}
|
||
|
|
for _, n := range in.PathParams {
|
||
|
|
n = strings.TrimSpace(n)
|
||
|
|
if n != "" {
|
||
|
|
pathParams[n] = "{{" + n + "}}"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return CandidateTool{
|
||
|
|
Name: name,
|
||
|
|
Description: strings.TrimSpace(in.Description),
|
||
|
|
Parameters: paramsJSON,
|
||
|
|
Endpoint: Endpoint{
|
||
|
|
Method: method,
|
||
|
|
URL: strings.TrimSpace(in.URL),
|
||
|
|
Path: pathParams,
|
||
|
|
Query: query,
|
||
|
|
Header: in.Headers,
|
||
|
|
},
|
||
|
|
}, nil
|
||
|
|
}
|