WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CastParams performs schema-driven type casting on tool arguments.
|
||||
// LLMs sometimes return incorrect types (e.g., "true" instead of true, "123" instead of 123).
|
||||
// This function attempts safe conversions based on the JSON Schema definition of the tool's parameters.
|
||||
//
|
||||
// If the schema is nil or cannot be parsed, the original args are returned unchanged.
|
||||
func CastParams(args json.RawMessage, schema json.RawMessage) json.RawMessage {
|
||||
if len(schema) == 0 || len(args) == 0 {
|
||||
return args
|
||||
}
|
||||
|
||||
var schemaDef map[string]interface{}
|
||||
if err := json.Unmarshal(schema, &schemaDef); err != nil {
|
||||
return args
|
||||
}
|
||||
|
||||
properties, ok := schemaDef["properties"].(map[string]interface{})
|
||||
if !ok || len(properties) == 0 {
|
||||
return args
|
||||
}
|
||||
|
||||
var argsMap map[string]interface{}
|
||||
if err := json.Unmarshal(args, &argsMap); err != nil {
|
||||
return args
|
||||
}
|
||||
|
||||
changed := false
|
||||
for key, val := range argsMap {
|
||||
propDef, exists := properties[key]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
prop, ok := propDef.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
targetType, _ := prop["type"].(string)
|
||||
if targetType == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
newVal, didCast := castValue(val, targetType)
|
||||
if didCast {
|
||||
argsMap[key] = newVal
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
|
||||
if !changed {
|
||||
return args
|
||||
}
|
||||
|
||||
result, err := json.Marshal(argsMap)
|
||||
if err != nil {
|
||||
return args
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// castValue attempts to convert val to the expected targetType.
|
||||
// Returns (newValue, true) if a conversion was made, (val, false) otherwise.
|
||||
func castValue(val interface{}, targetType string) (interface{}, bool) {
|
||||
switch targetType {
|
||||
case "array":
|
||||
if s, ok := val.(string); ok {
|
||||
// Try JSON parsing first (handles "[{...}]" → []interface{})
|
||||
var parsed []interface{}
|
||||
if err := json.Unmarshal([]byte(s), &parsed); err == nil {
|
||||
return parsed, true
|
||||
}
|
||||
// Fall back: single string → string array
|
||||
return []string{s}, true
|
||||
}
|
||||
|
||||
case "boolean":
|
||||
if s, ok := val.(string); ok {
|
||||
lower := strings.ToLower(s)
|
||||
switch lower {
|
||||
case "true", "1", "yes":
|
||||
return true, true
|
||||
case "false", "0", "no":
|
||||
return false, true
|
||||
}
|
||||
}
|
||||
// JSON number 0/1 -> bool
|
||||
if n, ok := val.(float64); ok {
|
||||
if n == 0 {
|
||||
return false, true
|
||||
}
|
||||
if n == 1 {
|
||||
return true, true
|
||||
}
|
||||
}
|
||||
|
||||
case "integer":
|
||||
if s, ok := val.(string); ok {
|
||||
if i, err := strconv.ParseInt(s, 10, 64); err == nil {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
// JSON numbers are float64 in Go; convert to int if it's a whole number
|
||||
if f, ok := val.(float64); ok {
|
||||
if f == float64(int64(f)) {
|
||||
return int64(f), true
|
||||
}
|
||||
}
|
||||
|
||||
case "number":
|
||||
if s, ok := val.(string); ok {
|
||||
if f, err := strconv.ParseFloat(s, 64); err == nil {
|
||||
return f, true
|
||||
}
|
||||
}
|
||||
|
||||
case "string":
|
||||
// Non-string values -> string (e.g., number or bool passed as non-string)
|
||||
switch v := val.(type) {
|
||||
case bool:
|
||||
if v {
|
||||
return "true", true
|
||||
}
|
||||
return "false", true
|
||||
case float64:
|
||||
return strconv.FormatFloat(v, 'f', -1, 64), true
|
||||
case int64:
|
||||
return strconv.FormatInt(v, 10), true
|
||||
}
|
||||
}
|
||||
|
||||
return val, false
|
||||
}
|
||||
Reference in New Issue
Block a user