mirror of
https://github.com/cheikh2shift/godex.git
synced 2026-10-23 11:22:18 -05:00
Merge branch 'main' into betteralign
This commit is contained in:
30 files changed
+264
-361
No files matched your search
@@ -47,7 +47,7 @@ The server exposes a subset of GoDex’s filesystem tools, including:
|
||||
- `create_directory`, `delete_file`
|
||||
- `search_files`, `search_in_file`, `search_directory_text`, `search_file_text`
|
||||
- `get_file_info`, `read_file_line_range`
|
||||
- `replace_first_in_file`, `replace_line_range`, `insert_at_line`
|
||||
- `replace_first_in_file`, `insert_at_line`
|
||||
- `list_allowed_paths`
|
||||
|
||||
## Add to OpenCode
|
||||
|
||||
@@ -9,11 +9,6 @@ Orchestration and parallel tasks? open another terminal tab and start a new inst
|
||||
|
||||

|
||||
|
||||
## Requirements
|
||||
|
||||
- **Go 1.25.7+** - Build from source
|
||||
- **Ollama** - For the default LLM backend (or use Gemini, OpenRouter, or llama.cpp)
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Requirements](#requirements)
|
||||
|
||||
+62
-62
@@ -15,7 +15,6 @@ import (
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -35,6 +34,7 @@ import (
|
||||
"github.com/cheikh2shift/godex/internal/mcp"
|
||||
"github.com/cheikh2shift/godex/internal/ml"
|
||||
"github.com/cheikh2shift/godex/internal/providers"
|
||||
"github.com/cheikh2shift/godex/internal/rxcache"
|
||||
"github.com/cheikh2shift/godex/internal/scheduler"
|
||||
"github.com/cheikh2shift/godex/internal/wizard"
|
||||
"github.com/cheikh2shift/godex/modelquery"
|
||||
@@ -53,7 +53,7 @@ var ErrUserAborted = errors.New("user aborted")
|
||||
type MCPServer interface {
|
||||
Name() string
|
||||
Tools() []mcp.Tool
|
||||
CallTool(ctx context.Context, name string, args map[string]interface{}) (string, error)
|
||||
CallTool(ctx context.Context, name string, args map[string]any) (string, error)
|
||||
AllowedPaths() []string
|
||||
AddPath(ctx context.Context, path string) error
|
||||
TempAddPath(path string)
|
||||
@@ -305,9 +305,9 @@ func main() {
|
||||
var providerTools []providers.Tool
|
||||
for _, server := range servers {
|
||||
for _, tool := range server.Tools() {
|
||||
var inputSchema map[string]interface{}
|
||||
var inputSchema map[string]any
|
||||
if err := json.Unmarshal(tool.InputSchema, &inputSchema); err != nil {
|
||||
inputSchema = map[string]interface{}{}
|
||||
inputSchema = map[string]any{}
|
||||
}
|
||||
providerTools = append(providerTools, providers.Tool{
|
||||
Name: tool.Name,
|
||||
@@ -342,7 +342,7 @@ func main() {
|
||||
playSound()
|
||||
})
|
||||
sched.SetProviderGetter(&schedulerProviderGetter{provider: provider})
|
||||
var serverSlice []interface{} = make([]interface{}, len(servers))
|
||||
var serverSlice []any = make([]any, len(servers))
|
||||
for i, s := range servers {
|
||||
serverSlice[i] = s
|
||||
}
|
||||
@@ -351,9 +351,9 @@ func main() {
|
||||
schedulerServer := mcp.NewSchedulerServer(sched, &schedulerProviderGetter{provider: provider})
|
||||
servers = append(servers, schedulerServer)
|
||||
for _, tool := range schedulerServer.Tools() {
|
||||
var inputSchema map[string]interface{}
|
||||
var inputSchema map[string]any
|
||||
if err := json.Unmarshal(tool.InputSchema, &inputSchema); err != nil {
|
||||
inputSchema = map[string]interface{}{}
|
||||
inputSchema = map[string]any{}
|
||||
}
|
||||
providerTools = append(providerTools, providers.Tool{
|
||||
Name: tool.Name,
|
||||
@@ -439,9 +439,9 @@ func main() {
|
||||
var providerTools []providers.Tool
|
||||
for _, server := range servers {
|
||||
for _, tool := range server.Tools() {
|
||||
var inputSchema map[string]interface{}
|
||||
var inputSchema map[string]any
|
||||
if err := json.Unmarshal(tool.InputSchema, &inputSchema); err != nil {
|
||||
inputSchema = map[string]interface{}{}
|
||||
inputSchema = map[string]any{}
|
||||
}
|
||||
providerTools = append(providerTools, providers.Tool{
|
||||
Name: tool.Name,
|
||||
@@ -1087,7 +1087,7 @@ promptLoop:
|
||||
hasError := false
|
||||
for _, tc := range toolCalls {
|
||||
toolName := tc["name"].(string)
|
||||
args := tc["arguments"].(map[string]interface{})
|
||||
args := tc["arguments"].(map[string]any)
|
||||
|
||||
if toolName == "read_image" && input != "" {
|
||||
if _, ok := args["prompt"]; !ok {
|
||||
@@ -1114,7 +1114,7 @@ promptLoop:
|
||||
log.Printf("_raw found, unwrapping: type=%T", raw)
|
||||
if rawStr, ok := raw.(string); ok {
|
||||
log.Printf("_raw is string, length=%d", len(rawStr))
|
||||
var rawMap map[string]interface{}
|
||||
var rawMap map[string]any
|
||||
if err := json.Unmarshal([]byte(rawStr), &rawMap); err == nil {
|
||||
log.Printf("_raw unwrapped successfully: %+v", rawMap)
|
||||
args = rawMap
|
||||
@@ -1445,8 +1445,8 @@ func handleMCPSubcommand(args []string, configPath string) error {
|
||||
if err := config.Save(configPath, cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Added MCP server %q to provider %q\n", serverName, provider.Name)
|
||||
return nil
|
||||
fmt.Printf("Added MCP server %q to provider %q\n", serverName, provider.Name)
|
||||
return nil
|
||||
case "serve":
|
||||
return handleMCPServeSubcommand(args[1:])
|
||||
case "remove":
|
||||
@@ -1509,11 +1509,11 @@ type mcpLog struct {
|
||||
lines []string
|
||||
}
|
||||
|
||||
func (m *mcpLog) Printf(format string, args ...interface{}) {
|
||||
func (m *mcpLog) Printf(format string, args ...any) {
|
||||
m.lines = append(m.lines, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (m *mcpLog) Println(args ...interface{}) {
|
||||
func (m *mcpLog) Println(args ...any) {
|
||||
m.lines = append(m.lines, fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
@@ -1854,7 +1854,7 @@ func handleKill(servers []MCPServer, input string) {
|
||||
|
||||
for _, server := range servers {
|
||||
if bs, ok := server.(*mcp.BashServer); ok {
|
||||
result, err := bs.CallTool(context.Background(), "kill_command", map[string]interface{}{
|
||||
result, err := bs.CallTool(context.Background(), "kill_command", map[string]any{
|
||||
"pid": float64(pid),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -2269,7 +2269,7 @@ func runToolLoop(ctx context.Context, provider *config.Provider, servers []MCPSe
|
||||
hasError := false
|
||||
for _, tc := range toolCalls {
|
||||
toolName := tc["name"].(string)
|
||||
args := tc["arguments"].(map[string]interface{})
|
||||
args := tc["arguments"].(map[string]any)
|
||||
|
||||
if toolName == "read_image" && input != "" {
|
||||
if _, ok := args["prompt"]; !ok {
|
||||
@@ -2292,7 +2292,7 @@ func runToolLoop(ctx context.Context, provider *config.Provider, servers []MCPSe
|
||||
|
||||
if raw, ok := args["_raw"]; ok {
|
||||
if rawStr, ok := raw.(string); ok {
|
||||
var rawMap map[string]interface{}
|
||||
var rawMap map[string]any
|
||||
if err := json.Unmarshal([]byte(rawStr), &rawMap); err == nil {
|
||||
args = rawMap
|
||||
}
|
||||
@@ -2414,9 +2414,9 @@ func buildProviderTools(servers []MCPServer) []providers.Tool {
|
||||
var providerTools []providers.Tool
|
||||
for _, server := range servers {
|
||||
for _, tool := range server.Tools() {
|
||||
var inputSchema map[string]interface{}
|
||||
var inputSchema map[string]any
|
||||
if err := json.Unmarshal(tool.InputSchema, &inputSchema); err != nil {
|
||||
inputSchema = map[string]interface{}{}
|
||||
inputSchema = map[string]any{}
|
||||
}
|
||||
providerTools = append(providerTools, providers.Tool{
|
||||
Name: tool.Name,
|
||||
@@ -2506,7 +2506,7 @@ func looksLikeToolCall(s string) bool {
|
||||
return strings.Contains(s, "{") || strings.Contains(s, "}")
|
||||
}
|
||||
|
||||
func callTool(ctx context.Context, servers []MCPServer, name string, args map[string]interface{}, timeoutSecs int, autoDenyRestrictedPaths bool) (string, error) {
|
||||
func callTool(ctx context.Context, servers []MCPServer, name string, args map[string]any, timeoutSecs int, autoDenyRestrictedPaths bool) (string, error) {
|
||||
|
||||
//log.Println("args", args)
|
||||
normalizeToolPathArgs(name, args)
|
||||
@@ -2557,7 +2557,7 @@ func callTool(ctx context.Context, servers []MCPServer, name string, args map[st
|
||||
return "", fmt.Errorf("tool %s not found", name)
|
||||
}
|
||||
|
||||
func executeToolCallsInParallel(ctx context.Context, servers []MCPServer, toolCalls []map[string]interface{}, timeoutSecs int, supportsNativeToolCalls bool, verbose bool, autoDenyRestrictedPaths bool, promptContext string) ([]string, bool) {
|
||||
func executeToolCallsInParallel(ctx context.Context, servers []MCPServer, toolCalls []map[string]any, timeoutSecs int, supportsNativeToolCalls bool, verbose bool, autoDenyRestrictedPaths bool, promptContext string) ([]string, bool) {
|
||||
if len(toolCalls) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
@@ -2572,7 +2572,7 @@ func executeToolCallsInParallel(ctx context.Context, servers []MCPServer, toolCa
|
||||
|
||||
for i, tc := range toolCalls {
|
||||
toolName := tc["name"].(string)
|
||||
args := tc["arguments"].(map[string]interface{})
|
||||
args := tc["arguments"].(map[string]any)
|
||||
var argsStr []string
|
||||
for k, v := range args {
|
||||
argsStr = append(argsStr, fmt.Sprintf("%s=%v", k, v))
|
||||
@@ -2594,11 +2594,11 @@ func executeToolCallsInParallel(ctx context.Context, servers []MCPServer, toolCa
|
||||
|
||||
for i, tc := range toolCalls {
|
||||
wg.Add(1)
|
||||
go func(index int, call map[string]interface{}, autoDeny bool) {
|
||||
go func(index int, call map[string]any, autoDeny bool) {
|
||||
defer wg.Done()
|
||||
|
||||
toolName := call["name"].(string)
|
||||
args := call["arguments"].(map[string]interface{})
|
||||
args := call["arguments"].(map[string]any)
|
||||
|
||||
if toolName == "read_image" && promptContext != "" {
|
||||
if _, ok := args["prompt"]; !ok {
|
||||
@@ -2731,7 +2731,7 @@ func showPathRestrictionPrompt(path string, allowedPaths []string) int {
|
||||
return selectOptionPrompt("Path Restricted", options)
|
||||
}
|
||||
|
||||
func normalizeToolPathArgs(toolName string, args map[string]interface{}) {
|
||||
func normalizeToolPathArgs(toolName string, args map[string]any) {
|
||||
if !isFilesystemTool(toolName) {
|
||||
return
|
||||
}
|
||||
@@ -2795,8 +2795,8 @@ func truncate(s string, maxLen int) string {
|
||||
return s[:maxLen] + "..."
|
||||
}
|
||||
|
||||
func extractAllToolCalls(text string) []map[string]interface{} {
|
||||
var results []map[string]interface{}
|
||||
func extractAllToolCalls(text string) []map[string]any {
|
||||
var results []map[string]any
|
||||
|
||||
candidates := []string{text}
|
||||
if normalized := normalizeToolCallText(text); normalized != text {
|
||||
@@ -2810,7 +2810,7 @@ func extractAllToolCalls(text string) []map[string]interface{} {
|
||||
for _, content := range codeBlocks {
|
||||
content = strings.TrimSpace(content)
|
||||
// Try to parse the whole block as one JSON object
|
||||
var data map[string]interface{}
|
||||
var data map[string]any
|
||||
if err := json.Unmarshal([]byte(content), &data); err == nil {
|
||||
if _, ok := data["name"].(string); ok {
|
||||
results = append(results, processToolData(data))
|
||||
@@ -2824,7 +2824,7 @@ func extractAllToolCalls(text string) []map[string]interface{} {
|
||||
}
|
||||
// If not a single object, try sanitizing multi-line strings
|
||||
sanitized := sanitizeMultilineJson(content)
|
||||
var sanitizedData map[string]interface{}
|
||||
var sanitizedData map[string]any
|
||||
if err := json.Unmarshal([]byte(sanitized), &sanitizedData); err == nil {
|
||||
if _, ok := sanitizedData["name"].(string); ok {
|
||||
results = append(results, processToolData(sanitizedData))
|
||||
@@ -2852,13 +2852,13 @@ func extractAllToolCalls(text string) []map[string]interface{} {
|
||||
|
||||
// Safety fallback for the specific [TOOL_CALL] format
|
||||
if len(results) == 0 {
|
||||
toolCallRe := regexp.MustCompile(`(?s)\[TOOL_CALL\]\s*(.*?)\s*\[/TOOL_CALL\]`)
|
||||
toolCallRe := rxcache.MustCompile(`(?s)\[TOOL_CALL\]\s*(.*?)\s*\[/TOOL_CALL\]`)
|
||||
tcMatches := toolCallRe.FindAllStringSubmatch(candidate, -1)
|
||||
for _, m := range tcMatches {
|
||||
if len(m) > 1 {
|
||||
nameRe := regexp.MustCompile(`tool\s*=>\s*"([^"]+)"`)
|
||||
nameRe := rxcache.MustCompile(`tool\s*=>\s*"([^"]+)"`)
|
||||
if nameMatch := nameRe.FindStringSubmatch(m[1]); len(nameMatch) > 1 {
|
||||
results = append(results, map[string]interface{}{"name": nameMatch[1], "arguments": map[string]interface{}{}})
|
||||
results = append(results, map[string]any{"name": nameMatch[1], "arguments": map[string]any{}})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2870,11 +2870,11 @@ func extractAllToolCalls(text string) []map[string]interface{} {
|
||||
for _, m := range nativeMatches {
|
||||
name := m[0]
|
||||
argsStr := m[1]
|
||||
var args map[string]interface{}
|
||||
var args map[string]any
|
||||
if err := json.Unmarshal([]byte(argsStr), &args); err != nil {
|
||||
args = map[string]interface{}{"_raw": argsStr}
|
||||
args = map[string]any{"_raw": argsStr}
|
||||
}
|
||||
results = append(results, map[string]interface{}{"name": name, "arguments": args})
|
||||
results = append(results, map[string]any{"name": name, "arguments": args})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2886,16 +2886,16 @@ func extractAllToolCalls(text string) []map[string]interface{} {
|
||||
return results
|
||||
}
|
||||
|
||||
func extractToolCallsFromJSONArray(text string) []map[string]interface{} {
|
||||
func extractToolCallsFromJSONArray(text string) []map[string]any {
|
||||
trimmed := strings.TrimSpace(text)
|
||||
if trimmed == "" {
|
||||
return nil
|
||||
}
|
||||
var items []map[string]interface{}
|
||||
var items []map[string]any
|
||||
if err := json.Unmarshal([]byte(trimmed), &items); err != nil {
|
||||
return nil
|
||||
}
|
||||
var results []map[string]interface{}
|
||||
var results []map[string]any
|
||||
for _, item := range items {
|
||||
if item == nil {
|
||||
continue
|
||||
@@ -2907,11 +2907,11 @@ func extractToolCallsFromJSONArray(text string) []map[string]interface{} {
|
||||
if tool, ok := item["tool"].(string); ok {
|
||||
args := parseToolArgs(item)
|
||||
if len(args) == 0 {
|
||||
if params, ok := item["parameters"].(map[string]interface{}); ok {
|
||||
if params, ok := item["parameters"].(map[string]any); ok {
|
||||
args = params
|
||||
}
|
||||
}
|
||||
results = append(results, map[string]interface{}{
|
||||
results = append(results, map[string]any{
|
||||
"name": tool,
|
||||
"arguments": args,
|
||||
})
|
||||
@@ -2922,7 +2922,7 @@ func extractToolCallsFromJSONArray(text string) []map[string]interface{} {
|
||||
|
||||
func extractNativeToolCalls(text string) [][2]string {
|
||||
var results [][2]string
|
||||
re := regexp.MustCompile(`\[TOOL_CALL:\s*([^\s|]+)\s*\|`)
|
||||
re := rxcache.MustCompile(`\[TOOL_CALL:\s*([^\s|]+)\s*\|`)
|
||||
matches := re.FindAllStringSubmatchIndex(text, -1)
|
||||
for _, m := range matches {
|
||||
if len(m) < 4 {
|
||||
@@ -2977,8 +2977,8 @@ func normalizeToolCallText(text string) string {
|
||||
|
||||
nonEmpty := 0
|
||||
withMargin := 0
|
||||
marginRe := regexp.MustCompile(`^\s*│`)
|
||||
stripRe := regexp.MustCompile(`^\s*│\s?`)
|
||||
marginRe := rxcache.MustCompile(`^\s*│`)
|
||||
stripRe := rxcache.MustCompile(`^\s*│\s?`)
|
||||
|
||||
for _, line := range lines {
|
||||
if strings.TrimSpace(line) == "" {
|
||||
@@ -3004,7 +3004,7 @@ func normalizeToolCallText(text string) string {
|
||||
// Only replaces the first occurrence at the start of the command
|
||||
func fixDriveLetterPath(cmd string) string {
|
||||
// Match :/<path> at start and convert to cd /<path>
|
||||
re := regexp.MustCompile(`(?i)^:\/(.+)`)
|
||||
re := rxcache.MustCompile(`(?i)^:\/(.+)`)
|
||||
match := re.FindStringSubmatch(cmd)
|
||||
if match == nil {
|
||||
return cmd
|
||||
@@ -3098,8 +3098,8 @@ func extractCodeBlockContent(text string) []string {
|
||||
}
|
||||
|
||||
// extractJsonObjects finds all balanced { ... } strings and tries to parse them as tool calls
|
||||
func extractJsonObjects(text string) []map[string]interface{} {
|
||||
var results []map[string]interface{}
|
||||
func extractJsonObjects(text string) []map[string]any {
|
||||
var results []map[string]any
|
||||
startIdx := -1
|
||||
braceCount := 0
|
||||
|
||||
@@ -3114,7 +3114,7 @@ func extractJsonObjects(text string) []map[string]interface{} {
|
||||
braceCount--
|
||||
if braceCount == 0 && startIdx != -1 {
|
||||
potentialJson := text[startIdx : i+1]
|
||||
var data map[string]interface{}
|
||||
var data map[string]any
|
||||
if err := json.Unmarshal([]byte(potentialJson), &data); err == nil {
|
||||
if _, ok := data["name"].(string); ok {
|
||||
results = append(results, processToolData(data))
|
||||
@@ -3128,7 +3128,7 @@ func extractJsonObjects(text string) []map[string]interface{} {
|
||||
}
|
||||
|
||||
func extractThinkingText(text string) string {
|
||||
codeBlockRe := regexp.MustCompile("(?s)```(?:json)?\\n.*?\\n```")
|
||||
codeBlockRe := rxcache.MustCompile("(?s)```(?:json)?\\n.*?\\n```")
|
||||
thinking := codeBlockRe.ReplaceAllString(text, "")
|
||||
thinking = strings.TrimSpace(thinking)
|
||||
return thinking
|
||||
@@ -3144,7 +3144,7 @@ func splitFinalAnswer(text string) (string, string, bool) {
|
||||
return pre, final, true
|
||||
}
|
||||
|
||||
func processToolData(data map[string]interface{}) map[string]interface{} {
|
||||
func processToolData(data map[string]any) map[string]any {
|
||||
name, _ := data["name"].(string)
|
||||
args := parseToolArgs(data)
|
||||
|
||||
@@ -3157,38 +3157,38 @@ func processToolData(data map[string]interface{}) map[string]interface{} {
|
||||
args[k] = s
|
||||
}
|
||||
}
|
||||
return map[string]interface{}{"name": name, "arguments": args}
|
||||
return map[string]any{"name": name, "arguments": args}
|
||||
}
|
||||
|
||||
func parseToolArgs(data map[string]interface{}) map[string]interface{} {
|
||||
if a, ok := data["arguments"].(map[string]interface{}); ok {
|
||||
func parseToolArgs(data map[string]any) map[string]any {
|
||||
if a, ok := data["arguments"].(map[string]any); ok {
|
||||
return a
|
||||
}
|
||||
if a, ok := data["args"].(map[string]interface{}); ok {
|
||||
if a, ok := data["args"].(map[string]any); ok {
|
||||
return a
|
||||
}
|
||||
if a, ok := data["parameters"].(map[string]interface{}); ok {
|
||||
if a, ok := data["parameters"].(map[string]any); ok {
|
||||
return a
|
||||
}
|
||||
if a, ok := data["params"].(map[string]interface{}); ok {
|
||||
if a, ok := data["params"].(map[string]any); ok {
|
||||
return a
|
||||
}
|
||||
if raw, ok := data["parameters"].(string); ok && strings.TrimSpace(raw) != "" {
|
||||
var parsed map[string]interface{}
|
||||
var parsed map[string]any
|
||||
if err := json.Unmarshal([]byte(raw), &parsed); err == nil {
|
||||
return parsed
|
||||
}
|
||||
}
|
||||
if raw, ok := data["arguments"].(string); ok && strings.TrimSpace(raw) != "" {
|
||||
var parsed map[string]interface{}
|
||||
var parsed map[string]any
|
||||
if err := json.Unmarshal([]byte(raw), &parsed); err == nil {
|
||||
return parsed
|
||||
}
|
||||
}
|
||||
return make(map[string]interface{})
|
||||
return make(map[string]any)
|
||||
}
|
||||
|
||||
func shouldExecuteToolCall(text string) ([]map[string]interface{}, bool) {
|
||||
func shouldExecuteToolCall(text string) ([]map[string]any, bool) {
|
||||
toolCalls := extractAllToolCalls(text)
|
||||
if len(toolCalls) == 0 {
|
||||
return nil, false
|
||||
@@ -3196,7 +3196,7 @@ func shouldExecuteToolCall(text string) ([]map[string]interface{}, bool) {
|
||||
return toolCalls, true
|
||||
}
|
||||
|
||||
func filterToolCallsByAvailability(servers []MCPServer, toolCalls []map[string]interface{}) ([]map[string]interface{}, []string) {
|
||||
func filterToolCallsByAvailability(servers []MCPServer, toolCalls []map[string]any) ([]map[string]any, []string) {
|
||||
if len(toolCalls) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -3206,7 +3206,7 @@ func filterToolCallsByAvailability(servers []MCPServer, toolCalls []map[string]i
|
||||
available[tool.Name] = struct{}{}
|
||||
}
|
||||
}
|
||||
var filtered []map[string]interface{}
|
||||
var filtered []map[string]any
|
||||
var missing []string
|
||||
for _, tc := range toolCalls {
|
||||
name, _ := tc["name"].(string)
|
||||
|
||||
+1
-11
@@ -79,7 +79,7 @@ func serveFilesystemMCP(baseAllowedPaths []string, autoConfirm bool, useRoots bo
|
||||
fs := mcp.NewFileSystemServer(allowed, autoConfirm)
|
||||
|
||||
args, _ := request.Params.Arguments.(map[string]any)
|
||||
argMap := make(map[string]interface{}, len(args))
|
||||
argMap := make(map[string]any, len(args))
|
||||
for k, v := range args {
|
||||
argMap[k] = v
|
||||
}
|
||||
@@ -172,16 +172,6 @@ func serveFilesystemMCP(baseAllowedPaths []string, autoConfirm bool, useRoots bo
|
||||
},
|
||||
Required: []string{"path", "find", "replace"},
|
||||
})
|
||||
addProxyTool("replace_line_range", "Replace a range of lines in a file.", mcplib.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"path": map[string]any{"type": "string", "description": "File path."},
|
||||
"start": map[string]any{"type": "number", "description": "Start line number (1-indexed)."},
|
||||
"end": map[string]any{"type": "number", "description": "End line number (inclusive)."},
|
||||
"new_content": map[string]any{"type": "string", "description": "New content to insert at the deleted location."},
|
||||
},
|
||||
Required: []string{"path", "start", "end", "new_content"},
|
||||
})
|
||||
addProxyTool("insert_at_line", "Insert content at a specific line number.", mcplib.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
|
||||
@@ -13,8 +13,8 @@ type schedulerProviderGetter struct {
|
||||
provider *config.Provider
|
||||
}
|
||||
|
||||
func (s *schedulerProviderGetter) GetProvider(cfg interface{}) (interface{}, error) {
|
||||
cfgMap, ok := cfg.(map[string]interface{})
|
||||
func (s *schedulerProviderGetter) GetProvider(cfg any) (any, error) {
|
||||
cfgMap, ok := cfg.(map[string]any)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid config type")
|
||||
}
|
||||
|
||||
@@ -89,15 +89,6 @@ DYNO_TOOL_CALL_BATCHES = [
|
||||
"content": "inserted line",
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "replace_line_range",
|
||||
"arguments": {
|
||||
"path": "/dyno/workspace/example_dir/example.txt",
|
||||
"start_line": 2,
|
||||
"end_line": 2,
|
||||
"new_content": "replaced second line",
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "search_directory_text",
|
||||
"arguments": {"path": "/dyno/workspace", "query": "replaced second line"},
|
||||
@@ -324,23 +315,6 @@ SYSTEM_TOOLS = [
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "replace_line_range",
|
||||
"description": "Replace a range of lines in a file",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"path": {"type": "string", "description": "File path"},
|
||||
"start": {"type": "integer", "description": "Start line"},
|
||||
"end": {"type": "integer", "description": "End line"},
|
||||
"new": {"type": "string", "description": "New content"}
|
||||
},
|
||||
"required": ["path", "start", "end", "new"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
|
||||
+11
-10
@@ -10,11 +10,12 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/cheikh2shift/godex/internal/rxcache"
|
||||
)
|
||||
|
||||
var runCmdMu sync.Mutex
|
||||
@@ -178,9 +179,9 @@ type BashServer struct {
|
||||
}
|
||||
|
||||
var (
|
||||
cdRegex = regexp.MustCompile(`(?i)^cd\s+(\S+)`)
|
||||
interpreterRegex = regexp.MustCompile(`(?i)(^|\s)(python[0-9.]*|pypy|ruby|perl|python|bash|sh|zsh|fish|r|lua|lua5|tcl|expect)(\s|$)`)
|
||||
pathRegex = regexp.MustCompile(`(?:^|\s)([/\\]+[^\s/\\]+(?:/[^\s/\\]*)*|[\.]{1,2}[/\\]|(?:~|\$(?:HOME|\w+))[/\\][^\s/\\]+)`)
|
||||
cdRegex = rxcache.MustCompile(`(?i)^cd\s+(\S+)`)
|
||||
interpreterRegex = rxcache.MustCompile(`(?i)(^|\s)(python[0-9.]*|pypy|ruby|perl|python|bash|sh|zsh|fish|r|lua|lua5|tcl|expect)(\s|$)`)
|
||||
pathRegex = rxcache.MustCompile(`(?:^|\s)([/\\]+[^\s/\\]+(?:/[^\s/\\]*)*|[\.]{1,2}[/\\]|(?:~|\$(?:HOME|\w+))[/\\][^\s/\\]+)`)
|
||||
)
|
||||
|
||||
func extractPathsFromCommand(command string) []string {
|
||||
@@ -307,7 +308,7 @@ func (s *BashServer) Tools() []Tool {
|
||||
return s.tools
|
||||
}
|
||||
|
||||
func (s *BashServer) CallTool(ctx context.Context, name string, arguments map[string]interface{}) (string, error) {
|
||||
func (s *BashServer) CallTool(ctx context.Context, name string, arguments map[string]any) (string, error) {
|
||||
switch name {
|
||||
case "run_command":
|
||||
runCmdMu.Lock()
|
||||
@@ -340,7 +341,7 @@ func (s *BashServer) isPathAllowed(path string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *BashServer) runCommand(ctx context.Context, args map[string]interface{}) (string, error) {
|
||||
func (s *BashServer) runCommand(ctx context.Context, args map[string]any) (string, error) {
|
||||
command, ok := args["command"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("command is required")
|
||||
@@ -625,7 +626,7 @@ func (s *BashServer) runSyncCommand(ctx context.Context, command string, timeout
|
||||
return stdoutStr, nil
|
||||
}
|
||||
|
||||
func (s *BashServer) killCommand(args map[string]interface{}) (string, error) {
|
||||
func (s *BashServer) killCommand(args map[string]any) (string, error) {
|
||||
// Try job_id first (new format)
|
||||
jobID, hasJobID := args["job_id"].(string)
|
||||
|
||||
@@ -665,7 +666,7 @@ func (s *BashServer) killCommand(args map[string]interface{}) (string, error) {
|
||||
return fmt.Sprintf("Killed job %s", jobID), nil
|
||||
}
|
||||
|
||||
func (s *BashServer) killAllBackground(args map[string]interface{}) (string, error) {
|
||||
func (s *BashServer) killAllBackground(args map[string]any) (string, error) {
|
||||
killed := s.jobTracker.KillAll()
|
||||
if len(killed) == 0 {
|
||||
return "No background jobs running", nil
|
||||
@@ -751,7 +752,7 @@ func (s *BashServer) GetJob(jobID string) (*Job, bool) {
|
||||
return s.jobTracker.Get(jobID)
|
||||
}
|
||||
|
||||
func (s *BashServer) runPython(ctx context.Context, args map[string]interface{}) (string, error) {
|
||||
func (s *BashServer) runPython(ctx context.Context, args map[string]any) (string, error) {
|
||||
code, ok := args["code"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("code is required: args=%v", args)
|
||||
@@ -777,7 +778,7 @@ func (s *BashServer) runPython(ctx context.Context, args map[string]interface{})
|
||||
return string(output), nil
|
||||
}
|
||||
|
||||
func (s *BashServer) runNode(ctx context.Context, args map[string]interface{}) (string, error) {
|
||||
func (s *BashServer) runNode(ctx context.Context, args map[string]any) (string, error) {
|
||||
code, ok := args["code"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("code is required: args=%v", args)
|
||||
|
||||
@@ -51,7 +51,7 @@ func GetUnixTools() []Tool {
|
||||
}
|
||||
|
||||
// HandleRunBashScript handles the run_bash_script command - only available on Unix
|
||||
func (s *BashServer) HandleRunBashScript(ctx context.Context, args map[string]interface{}) (string, error) {
|
||||
func (s *BashServer) HandleRunBashScript(ctx context.Context, args map[string]any) (string, error) {
|
||||
code, ok := args["code"].(string)
|
||||
if !ok || code == "" {
|
||||
return "", fmt.Errorf("code is required")
|
||||
|
||||
@@ -45,6 +45,6 @@ func GetUnixTools() []Tool {
|
||||
}
|
||||
|
||||
// HandleRunBashScript is not supported on Windows
|
||||
func (s *BashServer) HandleRunBashScript(ctx context.Context, args map[string]interface{}) (string, error) {
|
||||
func (s *BashServer) HandleRunBashScript(ctx context.Context, args map[string]any) (string, error) {
|
||||
return "", fmt.Errorf("run_bash_script is not supported on Windows")
|
||||
}
|
||||
@@ -233,11 +233,11 @@ func (m *MCPToolExecutor) AllowedPaths() []string {
|
||||
return paths
|
||||
}
|
||||
|
||||
func (m *MCPToolExecutor) CallTool(ctx context.Context, name string, arguments map[string]interface{}) (string, error) {
|
||||
func (m *MCPToolExecutor) CallTool(ctx context.Context, name string, arguments map[string]any) (string, error) {
|
||||
return m.callToolWithRetry(ctx, name, arguments, 0)
|
||||
}
|
||||
|
||||
func (m *MCPToolExecutor) callToolWithRetry(ctx context.Context, name string, arguments map[string]interface{}, attempt int) (string, error) {
|
||||
func (m *MCPToolExecutor) callToolWithRetry(ctx context.Context, name string, arguments map[string]any, attempt int) (string, error) {
|
||||
maxRetries := 1
|
||||
|
||||
if attempt >= maxRetries {
|
||||
@@ -269,14 +269,14 @@ func (m *MCPToolExecutor) callToolWithRetry(ctx context.Context, name string, ar
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (m *MCPToolExecutor) callToolWithCancellation(ctx context.Context, requestID int64, name string, arguments map[string]interface{}) (string, error) {
|
||||
func (m *MCPToolExecutor) callToolWithCancellation(ctx context.Context, requestID int64, name string, arguments map[string]any) (string, error) {
|
||||
t := m.mcpClient.GetTransport()
|
||||
|
||||
request := transport.JSONRPCRequest{
|
||||
JSONRPC: mcp.JSONRPC_VERSION,
|
||||
ID: mcp.NewRequestId(requestID),
|
||||
Method: "tools/call",
|
||||
Params: map[string]interface{}{
|
||||
Params: map[string]any{
|
||||
"name": name,
|
||||
"arguments": arguments,
|
||||
},
|
||||
|
||||
+22
-82
@@ -13,9 +13,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/cheikh2shift/godex/internal/ml"
|
||||
"github.com/cheikh2shift/godex/internal/rxcache"
|
||||
)
|
||||
|
||||
func getIntArg(args map[string]interface{}, key, alias string) (float64, bool) {
|
||||
func getIntArg(args map[string]any, key, alias string) (float64, bool) {
|
||||
if v, ok := args[key].(float64); ok {
|
||||
return v, true
|
||||
}
|
||||
@@ -106,11 +107,6 @@ func NewFileSystemServer(allowedPaths []string, autoConfirm bool) *FileSystemSer
|
||||
Description: "Replace the first occurrence of a text string in a file",
|
||||
InputSchema: json.RawMessage(`{"type":"object","properties":{"path":{"type":"string","description":"Absolute path to the file"},"find":{"type":"string","description":"Text to find (plain string, not regex)"},"replace":{"type":"string","description":"Replacement text"}},"required":["path","find","replace"]}`),
|
||||
},
|
||||
{
|
||||
Name: "replace_line_range",
|
||||
Description: "Delete a range of lines and insert new content at that location in a file",
|
||||
InputSchema: json.RawMessage(`{"type":"object","properties":{"path":{"type":"string","description":"Absolute path to the file"},"start":{"type":"integer","description":"Start line number (1-indexed)"},"end":{"type":"integer","description":"End line number (inclusive)"},"new_content":{"type":"string","description":"New content to insert at the deleted location"},"start_line":{"type":"integer","description":"Alias for start"},"end_line":{"type":"integer","description":"Alias for end"}},"required":["path","start","end","new_content"]}`),
|
||||
},
|
||||
{
|
||||
Name: "insert_at_line",
|
||||
Description: "Insert content at a specific line number",
|
||||
@@ -145,7 +141,7 @@ func (s *FileSystemServer) Tools() []Tool {
|
||||
return s.tools
|
||||
}
|
||||
|
||||
func (s *FileSystemServer) CallTool(ctx context.Context, name string, arguments map[string]interface{}) (string, error) {
|
||||
func (s *FileSystemServer) CallTool(ctx context.Context, name string, arguments map[string]any) (string, error) {
|
||||
switch name {
|
||||
case "read_file":
|
||||
return s.readFile(arguments)
|
||||
@@ -171,8 +167,6 @@ func (s *FileSystemServer) CallTool(ctx context.Context, name string, arguments
|
||||
return s.readFileLineRange(arguments)
|
||||
case "replace_first_in_file":
|
||||
return s.replaceFirstInFile(arguments)
|
||||
case "replace_line_range":
|
||||
return s.replaceLineRange(arguments)
|
||||
case "insert_at_line":
|
||||
return s.insertAtLine(arguments)
|
||||
case "search_file_text":
|
||||
@@ -216,7 +210,7 @@ func (s *FileSystemServer) checkAndMaybeAddPath(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FileSystemServer) readFile(args map[string]interface{}) (string, error) {
|
||||
func (s *FileSystemServer) readFile(args map[string]any) (string, error) {
|
||||
path, ok := args["path"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("path is required")
|
||||
@@ -234,7 +228,7 @@ func (s *FileSystemServer) readFile(args map[string]interface{}) (string, error)
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
func (s *FileSystemServer) readImage(args map[string]interface{}) (string, error) {
|
||||
func (s *FileSystemServer) readImage(args map[string]any) (string, error) {
|
||||
path, ok := args["path"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("path is required")
|
||||
@@ -254,7 +248,7 @@ func (s *FileSystemServer) readImage(args map[string]interface{}) (string, error
|
||||
return summary, nil
|
||||
}
|
||||
|
||||
func (s *FileSystemServer) readText(args map[string]interface{}) (string, error) {
|
||||
func (s *FileSystemServer) readText(args map[string]any) (string, error) {
|
||||
path, ok := args["path"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("path is required")
|
||||
@@ -274,7 +268,7 @@ func (s *FileSystemServer) readText(args map[string]interface{}) (string, error)
|
||||
return text, nil
|
||||
}
|
||||
|
||||
func (s *FileSystemServer) readPdf(args map[string]interface{}) (string, error) {
|
||||
func (s *FileSystemServer) readPdf(args map[string]any) (string, error) {
|
||||
path, ok := args["path"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("path is required")
|
||||
@@ -296,7 +290,7 @@ func (s *FileSystemServer) readPdf(args map[string]interface{}) (string, error)
|
||||
return text, nil
|
||||
}
|
||||
|
||||
func (s *FileSystemServer) writeFile(args map[string]interface{}) (string, error) {
|
||||
func (s *FileSystemServer) writeFile(args map[string]any) (string, error) {
|
||||
path, ok := args["path"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("path is required")
|
||||
@@ -322,7 +316,7 @@ func (s *FileSystemServer) writeFile(args map[string]interface{}) (string, error
|
||||
return fmt.Sprintf("Written %d bytes to %s", len(content), path), nil
|
||||
}
|
||||
|
||||
func (s *FileSystemServer) listDirectory(args map[string]interface{}) (string, error) {
|
||||
func (s *FileSystemServer) listDirectory(args map[string]any) (string, error) {
|
||||
path, ok := args["path"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("path is required")
|
||||
@@ -363,7 +357,7 @@ func (s *FileSystemServer) listDirectory(args map[string]interface{}) (string, e
|
||||
return result.String(), nil
|
||||
}
|
||||
|
||||
func (s *FileSystemServer) createDirectory(args map[string]interface{}) (string, error) {
|
||||
func (s *FileSystemServer) createDirectory(args map[string]any) (string, error) {
|
||||
path, ok := args["path"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("path is required")
|
||||
@@ -380,7 +374,7 @@ func (s *FileSystemServer) createDirectory(args map[string]interface{}) (string,
|
||||
return fmt.Sprintf("Created directory: %s", path), nil
|
||||
}
|
||||
|
||||
func (s *FileSystemServer) deleteFile(args map[string]interface{}) (string, error) {
|
||||
func (s *FileSystemServer) deleteFile(args map[string]any) (string, error) {
|
||||
path, ok := args["path"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("path is required")
|
||||
@@ -409,7 +403,7 @@ func (s *FileSystemServer) deleteFile(args map[string]interface{}) (string, erro
|
||||
return fmt.Sprintf("Deleted file: %s", path), nil
|
||||
}
|
||||
|
||||
func (s *FileSystemServer) searchFiles(args map[string]interface{}) (string, error) {
|
||||
func (s *FileSystemServer) searchFiles(args map[string]any) (string, error) {
|
||||
path, ok := args["path"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("path is required")
|
||||
@@ -447,7 +441,7 @@ func (s *FileSystemServer) searchFiles(args map[string]interface{}) (string, err
|
||||
return strings.Join(matches, "\n"), nil
|
||||
}
|
||||
|
||||
func (s *FileSystemServer) getFileInfo(args map[string]interface{}) (string, error) {
|
||||
func (s *FileSystemServer) getFileInfo(args map[string]any) (string, error) {
|
||||
path, ok := args["path"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("path is required")
|
||||
@@ -466,7 +460,7 @@ func (s *FileSystemServer) getFileInfo(args map[string]interface{}) (string, err
|
||||
info.Name(), info.Size(), info.IsDir(), info.ModTime().Format("2006-01-02 15:04:05")), nil
|
||||
}
|
||||
|
||||
func (s *FileSystemServer) readFileLineRange(args map[string]interface{}) (string, error) {
|
||||
func (s *FileSystemServer) readFileLineRange(args map[string]any) (string, error) {
|
||||
path, ok := args["path"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("path is required")
|
||||
@@ -507,7 +501,7 @@ func (s *FileSystemServer) readFileLineRange(args map[string]interface{}) (strin
|
||||
return strings.Join(lines[startIdx:endIdx], "\n"), nil
|
||||
}
|
||||
|
||||
func (s *FileSystemServer) replaceFirstInFile(args map[string]interface{}) (string, error) {
|
||||
func (s *FileSystemServer) replaceFirstInFile(args map[string]any) (string, error) {
|
||||
path, ok := args["path"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("path is required")
|
||||
@@ -547,61 +541,7 @@ func (s *FileSystemServer) replaceFirstInFile(args map[string]interface{}) (stri
|
||||
return fmt.Sprintf("Replaced first occurrence of %s in %s", find, path), nil
|
||||
}
|
||||
|
||||
func (s *FileSystemServer) replaceLineRange(args map[string]interface{}) (string, error) {
|
||||
path, ok := args["path"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("path is required")
|
||||
}
|
||||
|
||||
if err := s.checkAndMaybeAddPath(path); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read file: %w", err)
|
||||
}
|
||||
|
||||
lines := strings.Split(string(content), "\n")
|
||||
start, ok := getIntArg(args, "start", "start_line")
|
||||
if !ok {
|
||||
return "", fmt.Errorf("start is required")
|
||||
}
|
||||
end, ok := getIntArg(args, "end", "end_line")
|
||||
if !ok {
|
||||
return "", fmt.Errorf("end is required")
|
||||
}
|
||||
newContent, ok := args["new_content"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("new_content is required")
|
||||
}
|
||||
|
||||
startIdx := int(start) - 1
|
||||
endIdx := int(end)
|
||||
|
||||
if startIdx < 0 || startIdx >= len(lines) {
|
||||
return "", fmt.Errorf("start line out of range")
|
||||
}
|
||||
if endIdx < startIdx {
|
||||
return "", fmt.Errorf("end line must be greater than or equal to start line")
|
||||
}
|
||||
if endIdx > len(lines) {
|
||||
endIdx = len(lines)
|
||||
}
|
||||
|
||||
deletedLines := endIdx - startIdx
|
||||
newLines := strings.Split(newContent, "\n")
|
||||
|
||||
result := append(lines[:startIdx], append(newLines, lines[endIdx:]...)...)
|
||||
|
||||
if err := os.WriteFile(path, []byte(strings.Join(result, "\n")), 0644); err != nil {
|
||||
return "", fmt.Errorf("failed to write file: %w", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Replaced lines %d-%d (%d lines deleted) with %d lines in %s", int(start), int(end), deletedLines, len(newLines), path), nil
|
||||
}
|
||||
|
||||
func (s *FileSystemServer) insertAtLine(args map[string]interface{}) (string, error) {
|
||||
func (s *FileSystemServer) insertAtLine(args map[string]any) (string, error) {
|
||||
path, ok := args["path"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("path is required")
|
||||
@@ -640,7 +580,7 @@ func (s *FileSystemServer) insertAtLine(args map[string]interface{}) (string, er
|
||||
return fmt.Sprintf("Inserted content at line %d in %s", int(lineNum), path), nil
|
||||
}
|
||||
|
||||
func (s *FileSystemServer) searchFileText(args map[string]interface{}) (string, error) {
|
||||
func (s *FileSystemServer) searchFileText(args map[string]any) (string, error) {
|
||||
path, ok := args["path"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("path is required")
|
||||
@@ -664,7 +604,7 @@ func (s *FileSystemServer) searchFileText(args map[string]interface{}) (string,
|
||||
pattern = "(?i)" + pattern
|
||||
}
|
||||
var err error
|
||||
regex, err = regexp.Compile(pattern)
|
||||
regex, err = rxcache.Compile(pattern)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid regex pattern: %w", err)
|
||||
}
|
||||
@@ -715,7 +655,7 @@ func (s *FileSystemServer) searchFileText(args map[string]interface{}) (string,
|
||||
return strings.Join(results, "\n"), nil
|
||||
}
|
||||
|
||||
func (s *FileSystemServer) searchInFile(args map[string]interface{}) (string, error) {
|
||||
func (s *FileSystemServer) searchInFile(args map[string]any) (string, error) {
|
||||
path, ok := args["path"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("path is required")
|
||||
@@ -755,7 +695,7 @@ func (s *FileSystemServer) searchInFile(args map[string]interface{}) (string, er
|
||||
if !caseSensitive {
|
||||
compiledPattern = "(?i)" + compiledPattern
|
||||
}
|
||||
regex, err = regexp.Compile(compiledPattern)
|
||||
regex, err = rxcache.Compile(compiledPattern)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid regex pattern: %w", err)
|
||||
}
|
||||
@@ -789,7 +729,7 @@ func (s *FileSystemServer) searchInFile(args map[string]interface{}) (string, er
|
||||
return strings.Join(matches, "\n"), nil
|
||||
}
|
||||
|
||||
func (s *FileSystemServer) searchDirectoryText(args map[string]interface{}) (string, error) {
|
||||
func (s *FileSystemServer) searchDirectoryText(args map[string]any) (string, error) {
|
||||
path, ok := args["path"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("path is required")
|
||||
@@ -819,7 +759,7 @@ func (s *FileSystemServer) searchDirectoryText(args map[string]interface{}) (str
|
||||
compiledPattern = "(?i)" + compiledPattern
|
||||
}
|
||||
var err error
|
||||
regex, err = regexp.Compile(compiledPattern)
|
||||
regex, err = rxcache.Compile(compiledPattern)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid regex pattern: %w", err)
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ func TestFileSystemServer_DefaultAllowsCwd(t *testing.T) {
|
||||
}
|
||||
|
||||
server := NewFileSystemServer(nil, false)
|
||||
out, err := server.CallTool(context.Background(), "list_directory", map[string]interface{}{
|
||||
out, err := server.CallTool(context.Background(), "list_directory", map[string]any{
|
||||
"path": ".",
|
||||
})
|
||||
if err != nil {
|
||||
@@ -68,7 +68,7 @@ func TestFileSystemServer_ListDirectory_DoesNotPanicOnInfoError(t *testing.T) {
|
||||
}
|
||||
|
||||
server := NewFileSystemServer([]string{tmp}, false)
|
||||
out, err := server.CallTool(context.Background(), "list_directory", map[string]interface{}{
|
||||
out, err := server.CallTool(context.Background(), "list_directory", map[string]any{
|
||||
"path": tmp,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -36,7 +36,7 @@ func (s *HiveServer) Tools() []Tool {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *HiveServer) CallTool(ctx context.Context, name string, args map[string]interface{}) (string, error) {
|
||||
func (s *HiveServer) CallTool(ctx context.Context, name string, args map[string]any) (string, error) {
|
||||
switch name {
|
||||
case "hive_list":
|
||||
instances, err := s.manager.Instances()
|
||||
@@ -55,7 +55,7 @@ func (s *HiveServer) CallTool(ctx context.Context, name string, args map[string]
|
||||
targetID, _ := args["target_id"].(string)
|
||||
|
||||
var requiredTools []string
|
||||
if toolsRaw, ok := args["required_tools"].([]interface{}); ok {
|
||||
if toolsRaw, ok := args["required_tools"].([]any); ok {
|
||||
for _, t := range toolsRaw {
|
||||
if s, ok := t.(string); ok {
|
||||
requiredTools = append(requiredTools, s)
|
||||
|
||||
+11
-11
@@ -14,18 +14,18 @@ type SchedulerServer struct {
|
||||
|
||||
type schedulerExt struct {
|
||||
scheduler interface {
|
||||
AddTask(prompt string, intervalSec int, runAt string, providerType, providerName, providerModel string) (interface{}, error)
|
||||
AddTask(prompt string, intervalSec int, runAt string, providerType, providerName, providerModel string) (any, error)
|
||||
StopTask(id string) bool
|
||||
RemoveTask(id string) bool
|
||||
ListTasks() []interface{}
|
||||
GetTask(id string) interface{}
|
||||
ListTasks() []any
|
||||
GetTask(id string) any
|
||||
}
|
||||
mu interface {
|
||||
Lock()
|
||||
Unlock()
|
||||
}
|
||||
provider interface {
|
||||
GetProvider(cfg interface{}) (interface{}, error)
|
||||
GetProvider(cfg any) (any, error)
|
||||
}
|
||||
providerMu interface {
|
||||
Lock()
|
||||
@@ -46,13 +46,13 @@ type TaskInfo struct {
|
||||
}
|
||||
|
||||
func NewSchedulerServer(scheduler interface {
|
||||
AddTask(prompt string, intervalSec int, runAt string, providerType, providerName, providerModel string) (interface{}, error)
|
||||
AddTask(prompt string, intervalSec int, runAt string, providerType, providerName, providerModel string) (any, error)
|
||||
StopTask(id string) bool
|
||||
RemoveTask(id string) bool
|
||||
ListTasks() []interface{}
|
||||
GetTask(id string) interface{}
|
||||
ListTasks() []any
|
||||
GetTask(id string) any
|
||||
}, providerGetter interface {
|
||||
GetProvider(cfg interface{}) (interface{}, error)
|
||||
GetProvider(cfg any) (any, error)
|
||||
}) *SchedulerServer {
|
||||
return &SchedulerServer{
|
||||
scheduler: &schedulerExt{
|
||||
@@ -97,7 +97,7 @@ func (s *SchedulerServer) Tools() []Tool {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SchedulerServer) CallTool(ctx context.Context, name string, args map[string]interface{}) (string, error) {
|
||||
func (s *SchedulerServer) CallTool(ctx context.Context, name string, args map[string]any) (string, error) {
|
||||
if name != "scheduler" {
|
||||
return "", fmt.Errorf("unknown tool: %s", name)
|
||||
}
|
||||
@@ -139,12 +139,12 @@ func (s *SchedulerServer) CallTool(ctx context.Context, name string, args map[st
|
||||
}
|
||||
|
||||
var (
|
||||
task interface{}
|
||||
task any
|
||||
err error
|
||||
)
|
||||
|
||||
type schedulerWithRepeat interface {
|
||||
AddTaskWithRepeat(prompt string, intervalSec int, runAt string, isRepeating bool, providerType, providerName, providerModel string) (interface{}, error)
|
||||
AddTaskWithRepeat(prompt string, intervalSec int, runAt string, isRepeating bool, providerType, providerName, providerModel string) (any, error)
|
||||
}
|
||||
if sr, ok := s.scheduler.scheduler.(schedulerWithRepeat); ok {
|
||||
task, err = sr.AddTaskWithRepeat(prompt, intervalSec, runAt, isRepeating, "current", "current", "current")
|
||||
|
||||
@@ -18,25 +18,25 @@ type fakeTask struct {
|
||||
IsRepeating bool `json:"is_repeating"`
|
||||
}
|
||||
|
||||
func (f *fakeScheduler) AddTask(prompt string, intervalSec int, runAt string, providerType, providerName, providerModel string) (interface{}, error) {
|
||||
func (f *fakeScheduler) AddTask(prompt string, intervalSec int, runAt string, providerType, providerName, providerModel string) (any, error) {
|
||||
return f.AddTaskWithRepeat(prompt, intervalSec, runAt, intervalSec > 0, providerType, providerName, providerModel)
|
||||
}
|
||||
|
||||
func (f *fakeScheduler) AddTaskWithRepeat(prompt string, intervalSec int, runAt string, isRepeating bool, providerType, providerName, providerModel string) (interface{}, error) {
|
||||
func (f *fakeScheduler) AddTaskWithRepeat(prompt string, intervalSec int, runAt string, isRepeating bool, providerType, providerName, providerModel string) (any, error) {
|
||||
f.lastIsRepeating = isRepeating
|
||||
return &fakeTask{ID: "ABCD", Prompt: prompt, IntervalSec: intervalSec, RunAt: runAt, IsRepeating: isRepeating}, nil
|
||||
}
|
||||
|
||||
func (f *fakeScheduler) StopTask(id string) bool { return true }
|
||||
func (f *fakeScheduler) RemoveTask(id string) bool { return true }
|
||||
func (f *fakeScheduler) ListTasks() []interface{} { return nil }
|
||||
func (f *fakeScheduler) GetTask(id string) interface{} { return nil }
|
||||
func (f *fakeScheduler) ListTasks() []any { return nil }
|
||||
func (f *fakeScheduler) GetTask(id string) any { return nil }
|
||||
|
||||
func TestSchedulerTool_RunOnceForcesNonRepeating(t *testing.T) {
|
||||
fs := &fakeScheduler{}
|
||||
server := NewSchedulerServer(fs, nil)
|
||||
|
||||
_, err := server.CallTool(context.Background(), "scheduler", map[string]interface{}{
|
||||
_, err := server.CallTool(context.Background(), "scheduler", map[string]any{
|
||||
"prompt": "hi",
|
||||
"interval_sec": float64(60),
|
||||
"run_once": true,
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cheikh2shift/godex/internal/rxcache"
|
||||
"github.com/chromedp/chromedp"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
@@ -100,7 +100,7 @@ func (s *WebScraperServer) checkAndMaybeAddURL(rawURL string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *WebScraperServer) CallTool(ctx context.Context, name string, arguments map[string]interface{}) (string, error) {
|
||||
func (s *WebScraperServer) CallTool(ctx context.Context, name string, arguments map[string]any) (string, error) {
|
||||
switch name {
|
||||
case "fetch_url":
|
||||
return s.fetchURL(arguments)
|
||||
@@ -111,7 +111,7 @@ func (s *WebScraperServer) CallTool(ctx context.Context, name string, arguments
|
||||
}
|
||||
}
|
||||
|
||||
func (s *WebScraperServer) fetchURL(args map[string]interface{}) (string, error) {
|
||||
func (s *WebScraperServer) fetchURL(args map[string]any) (string, error) {
|
||||
rawURL, ok := args["url"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("url is required")
|
||||
@@ -204,7 +204,7 @@ func (s *WebScraperServer) processContent(htmlContent, grepPattern string, start
|
||||
}
|
||||
|
||||
func (s *WebScraperServer) applyGrep(htmlContent, pattern string) (string, error) {
|
||||
re, err := regexp.Compile(pattern)
|
||||
re, err := rxcache.Compile(pattern)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid grep pattern: %v", err)
|
||||
}
|
||||
@@ -245,7 +245,7 @@ func (s *WebScraperServer) applyLineRange(content string, start, end float64) st
|
||||
return strings.Join(lines[startIdx:endIdx], "\n")
|
||||
}
|
||||
|
||||
func (s *WebScraperServer) getLinks(args map[string]interface{}) (string, error) {
|
||||
func (s *WebScraperServer) getLinks(args map[string]any) (string, error) {
|
||||
htmlContent, ok := args["html"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("html is required")
|
||||
|
||||
@@ -26,7 +26,7 @@ var (
|
||||
tesseractPath string
|
||||
)
|
||||
|
||||
func visionDebug(format string, args ...interface{}) {
|
||||
func visionDebug(format string, args ...any) {
|
||||
if DebugMode {
|
||||
log.Printf("[vision] "+format, args...)
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ func (g *geminiProvider) Cancel() {
|
||||
}
|
||||
}
|
||||
|
||||
func (g *geminiProvider) CallTool(ctx context.Context, name string, args map[string]interface{}) (string, error) {
|
||||
func (g *geminiProvider) CallTool(ctx context.Context, name string, args map[string]any) (string, error) {
|
||||
return "", fmt.Errorf("Gemini provider does not support direct tool calls")
|
||||
}
|
||||
|
||||
|
||||
@@ -154,7 +154,7 @@ func (h *huggingfaceProvider) Cancel() {
|
||||
}
|
||||
}
|
||||
|
||||
func (h *huggingfaceProvider) CallTool(ctx context.Context, name string, args map[string]interface{}) (string, error) {
|
||||
func (h *huggingfaceProvider) CallTool(ctx context.Context, name string, args map[string]any) (string, error) {
|
||||
return "", fmt.Errorf("huggingface provider does not support direct tool calls")
|
||||
}
|
||||
|
||||
|
||||
+20
-20
@@ -13,7 +13,6 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -22,6 +21,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/cheikh2shift/godex/internal/config"
|
||||
"github.com/cheikh2shift/godex/internal/rxcache"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -30,7 +30,7 @@ const (
|
||||
llamaRetryDelay = 5 * time.Second
|
||||
)
|
||||
|
||||
func llamaDebug(format string, args ...interface{}) {
|
||||
func llamaDebug(format string, args ...any) {
|
||||
if DebugMode {
|
||||
log.Printf("[llama.cpp] "+format, args...)
|
||||
}
|
||||
@@ -53,8 +53,8 @@ type llamaProvider struct {
|
||||
model string
|
||||
serverPath string
|
||||
tokenizePath string
|
||||
messages []map[string]interface{}
|
||||
tools []map[string]interface{}
|
||||
messages []map[string]any
|
||||
tools []map[string]any
|
||||
cancelGen uint64
|
||||
contextLimit int
|
||||
promptTokens int
|
||||
@@ -295,7 +295,7 @@ func resolveModelPathWithDownload(ctx context.Context, model string, downloadPro
|
||||
}
|
||||
|
||||
quantToFile := make(map[string]string)
|
||||
quantRe := regexp.MustCompile(`-([A-Za-z0-9_]+)\.gguf$`)
|
||||
quantRe := rxcache.MustCompile(`-([A-Za-z0-9_]+)\.gguf$`)
|
||||
for _, f := range ggufFiles {
|
||||
matches := quantRe.FindStringSubmatch(f)
|
||||
if len(matches) >= 2 {
|
||||
@@ -611,7 +611,7 @@ func newLlamaProviderWithProgress(cfg *config.Provider, downloadProgress func(Do
|
||||
cfg: cfgCopy,
|
||||
temperature: cfg.Temperature,
|
||||
client: &http.Client{Timeout: defaultLlamaServerTimeout},
|
||||
messages: []map[string]interface{}{},
|
||||
messages: []map[string]any{},
|
||||
contextLimit: cfg.ContextLimit,
|
||||
pendingToolCalls: make(map[string]string),
|
||||
serverPort: port,
|
||||
@@ -846,11 +846,11 @@ func (p *llamaProvider) Send(ctx context.Context, prompt string) (string, error)
|
||||
defer p.sendMu.Unlock()
|
||||
|
||||
p.mu.Lock()
|
||||
p.messages = append(p.messages, map[string]interface{}{
|
||||
p.messages = append(p.messages, map[string]any{
|
||||
"role": "user",
|
||||
"content": prompt,
|
||||
})
|
||||
messages := make([]map[string]interface{}, len(p.messages))
|
||||
messages := make([]map[string]any, len(p.messages))
|
||||
copy(messages, p.messages)
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
@@ -898,7 +898,7 @@ func (p *llamaProvider) Send(ctx context.Context, prompt string) (string, error)
|
||||
}
|
||||
|
||||
p.mu.Lock()
|
||||
p.messages = append(p.messages, map[string]interface{}{
|
||||
p.messages = append(p.messages, map[string]any{
|
||||
"role": "assistant",
|
||||
"content": response,
|
||||
})
|
||||
@@ -1005,8 +1005,8 @@ type llamaSendResult struct {
|
||||
totalCompletionTokens int
|
||||
}
|
||||
|
||||
func (p *llamaProvider) doSend(ctx context.Context, messages []map[string]interface{}) (llamaSendResult, error) {
|
||||
reqBody := map[string]interface{}{
|
||||
func (p *llamaProvider) doSend(ctx context.Context, messages []map[string]any) (llamaSendResult, error) {
|
||||
reqBody := map[string]any{
|
||||
"model": p.model,
|
||||
"messages": messages,
|
||||
"stream": true,
|
||||
@@ -1137,18 +1137,18 @@ func (p *llamaProvider) doSend(ctx context.Context, messages []map[string]interf
|
||||
if len(toolCalls) > 0 {
|
||||
var b strings.Builder
|
||||
for i, tc := range toolCalls {
|
||||
call := map[string]interface{}{
|
||||
call := map[string]any{
|
||||
"name": tc.Name,
|
||||
}
|
||||
if strings.TrimSpace(tc.Arguments) != "" {
|
||||
var args map[string]interface{}
|
||||
var args map[string]any
|
||||
if err := json.Unmarshal([]byte(tc.Arguments), &args); err == nil {
|
||||
call["arguments"] = args
|
||||
} else {
|
||||
call["arguments"] = map[string]interface{}{"_raw": tc.Arguments}
|
||||
call["arguments"] = map[string]any{"_raw": tc.Arguments}
|
||||
}
|
||||
} else {
|
||||
call["arguments"] = map[string]interface{}{}
|
||||
call["arguments"] = map[string]any{}
|
||||
}
|
||||
payload, err := json.Marshal(call)
|
||||
if err != nil {
|
||||
@@ -1266,7 +1266,7 @@ func (p *llamaProvider) Cancel() {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *llamaProvider) CallTool(ctx context.Context, name string, args map[string]interface{}) (string, error) {
|
||||
func (p *llamaProvider) CallTool(ctx context.Context, name string, args map[string]any) (string, error) {
|
||||
return "", fmt.Errorf("llama.cpp provider does not support direct tool calls, use MCP servers")
|
||||
}
|
||||
|
||||
@@ -1422,7 +1422,7 @@ func (p *llamaProvider) Reset() error {
|
||||
defer p.mu.Unlock()
|
||||
|
||||
p.client = &http.Client{Timeout: defaultLlamaServerTimeout}
|
||||
p.messages = []map[string]interface{}{}
|
||||
p.messages = []map[string]any{}
|
||||
p.promptTokens = 0
|
||||
p.completionTokens = 0
|
||||
p.pendingToolCalls = make(map[string]string)
|
||||
@@ -1433,14 +1433,14 @@ func (p *llamaProvider) SetMessages(messages []Message) error {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
p.messages = make([]map[string]interface{}, 0, len(messages))
|
||||
p.messages = make([]map[string]any, 0, len(messages))
|
||||
for _, msg := range messages {
|
||||
role := strings.TrimSpace(msg.Role)
|
||||
content := msg.Content
|
||||
if role == "" || content == "" {
|
||||
continue
|
||||
}
|
||||
p.messages = append(p.messages, map[string]interface{}{
|
||||
p.messages = append(p.messages, map[string]any{
|
||||
"role": role,
|
||||
"content": content,
|
||||
})
|
||||
@@ -1472,7 +1472,7 @@ func (p *llamaProvider) AppendMessages(messages []Message) error {
|
||||
if _, ok := seen[key]; ok {
|
||||
continue
|
||||
}
|
||||
p.messages = append(p.messages, map[string]interface{}{
|
||||
p.messages = append(p.messages, map[string]any{
|
||||
"role": role,
|
||||
"content": content,
|
||||
})
|
||||
|
||||
@@ -6,11 +6,12 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cheikh2shift/godex/internal/rxcache"
|
||||
)
|
||||
|
||||
func TestResolveModelPathLocal(t *testing.T) {
|
||||
@@ -36,7 +37,7 @@ func TestResolveModelPathLocal(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestQuantizationRegexParsing(t *testing.T) {
|
||||
re := regexp.MustCompile(`-([A-Za-z0-9_]+)\.gguf$`)
|
||||
re := rxcache.MustCompile(`-([A-Za-z0-9_]+)\.gguf$`)
|
||||
|
||||
tests := []struct {
|
||||
filename string
|
||||
@@ -513,7 +514,7 @@ func min(a, b int) int {
|
||||
}
|
||||
|
||||
func TestParseContextValueFromHTML(t *testing.T) {
|
||||
re := regexp.MustCompile(`(\d+)[Kk]\s*context\s*window`)
|
||||
re := rxcache.MustCompile(`(\d+)[Kk]\s*context\s*window`)
|
||||
|
||||
tests := []struct {
|
||||
html string
|
||||
|
||||
@@ -7,13 +7,13 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cheikh2shift/godex/internal/config"
|
||||
"github.com/cheikh2shift/godex/internal/rxcache"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -169,7 +169,7 @@ func (o *ollamaProvider) fetchOllamaLibraryContext() error {
|
||||
return err
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`(\d+)[Kk]\s*context\s*window`)
|
||||
re := rxcache.MustCompile(`(\d+)[Kk]\s*context\s*window`)
|
||||
matches := re.FindStringSubmatch(string(body))
|
||||
if len(matches) >= 2 {
|
||||
contextStr := matches[1]
|
||||
@@ -203,7 +203,7 @@ func GetOllamaContextLimit(model string) (int, error) {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`(\d+)[Kk]\s*context\s*window`)
|
||||
re := rxcache.MustCompile(`(\d+)[Kk]\s*context\s*window`)
|
||||
matches := re.FindStringSubmatch(string(body))
|
||||
if len(matches) >= 2 {
|
||||
contextStr := matches[1]
|
||||
@@ -475,7 +475,7 @@ func (o *ollamaProvider) Cancel() {
|
||||
}
|
||||
}
|
||||
|
||||
func (o *ollamaProvider) CallTool(ctx context.Context, name string, args map[string]interface{}) (string, error) {
|
||||
func (o *ollamaProvider) CallTool(ctx context.Context, name string, args map[string]any) (string, error) {
|
||||
return "", fmt.Errorf("Ollama provider does not support direct tool calls, use MCP servers configured in provider")
|
||||
}
|
||||
|
||||
|
||||
@@ -5,11 +5,12 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cheikh2shift/godex/internal/rxcache"
|
||||
)
|
||||
|
||||
func TestParseOllamaLibraryContextRegex(t *testing.T) {
|
||||
@@ -69,7 +70,7 @@ func TestParseOllamaLibraryContextRegex(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`(\d+)[Kk]\s*context\s*window`)
|
||||
re := rxcache.MustCompile(`(\d+)[Kk]\s*context\s*window`)
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -99,7 +100,7 @@ func TestParseOllamaLibraryContextRegex(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractContextFromOllamaHTML(t *testing.T) {
|
||||
re := regexp.MustCompile(`(\d+)[Kk]\s*context\s*window`)
|
||||
re := rxcache.MustCompile(`(\d+)[Kk]\s*context\s*window`)
|
||||
|
||||
realisticHTML := `
|
||||
<!DOCTYPE html>
|
||||
@@ -176,7 +177,7 @@ func TestExtractContextFromOllamaHTMLVariations(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`(\d+)[Kk]\s*context\s*window`)
|
||||
re := rxcache.MustCompile(`(\d+)[Kk]\s*context\s*window`)
|
||||
|
||||
for _, v := range variations {
|
||||
t.Run(v.name, func(t *testing.T) {
|
||||
@@ -205,7 +206,7 @@ func TestExtractContextNoMatch(t *testing.T) {
|
||||
`<div>just numbers 128K here</div>`,
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`(\d+)[Kk]\s*context\s*window`)
|
||||
re := rxcache.MustCompile(`(\d+)[Kk]\s*context\s*window`)
|
||||
|
||||
for _, html := range noMatchCases {
|
||||
matches := re.FindStringSubmatch(html)
|
||||
@@ -285,7 +286,7 @@ func TestFetchOllamaLibraryContextIntegration(t *testing.T) {
|
||||
}
|
||||
|
||||
client := &http.Client{Timeout: 30 * time.Second}
|
||||
re := regexp.MustCompile(`(\d+)[Kk]\s*context\s*window`)
|
||||
re := rxcache.MustCompile(`(\d+)[Kk]\s*context\s*window`)
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.model, func(t *testing.T) {
|
||||
|
||||
@@ -77,8 +77,8 @@ type openRouterProvider struct {
|
||||
model string
|
||||
apiKey string
|
||||
systemPrompt string
|
||||
messages []map[string]interface{}
|
||||
tools []map[string]interface{}
|
||||
messages []map[string]any
|
||||
tools []map[string]any
|
||||
cancelGen uint64
|
||||
contextLimit int
|
||||
promptTokens int
|
||||
@@ -186,7 +186,7 @@ func (p *openRouterProvider) Send(ctx context.Context, prompt string) (string, e
|
||||
}
|
||||
// Track only the user input in history
|
||||
if userInput != "" {
|
||||
p.messages = append(p.messages, map[string]interface{}{
|
||||
p.messages = append(p.messages, map[string]any{
|
||||
"role": "user",
|
||||
"content": userInput,
|
||||
})
|
||||
@@ -196,7 +196,7 @@ func (p *openRouterProvider) Send(ctx context.Context, prompt string) (string, e
|
||||
if maxHistory > 0 && len(p.messages) > maxHistory {
|
||||
p.messages = p.messages[len(p.messages)-maxHistory:]
|
||||
}
|
||||
messages := make([]map[string]interface{}, len(p.messages))
|
||||
messages := make([]map[string]any, len(p.messages))
|
||||
copy(messages, p.messages)
|
||||
p.mu.Unlock()
|
||||
|
||||
@@ -208,14 +208,14 @@ func (p *openRouterProvider) Send(ctx context.Context, prompt string) (string, e
|
||||
messages = prependSystemMessage(messages, systemMsg)
|
||||
}
|
||||
|
||||
reqBody := map[string]interface{}{
|
||||
reqBody := map[string]any{
|
||||
"model": p.model,
|
||||
"input": buildResponsesInput(messages),
|
||||
}
|
||||
|
||||
if len(p.tools) > 0 {
|
||||
reqBody["tools"] = p.tools
|
||||
reqBody["response_format"] = map[string]interface{}{
|
||||
reqBody["response_format"] = map[string]any{
|
||||
"type": "json_object",
|
||||
}
|
||||
}
|
||||
@@ -328,14 +328,14 @@ func (p *openRouterProvider) Send(ctx context.Context, prompt string) (string, e
|
||||
p.pendingToolCalls[tc.ID] = tc.Name
|
||||
}
|
||||
}
|
||||
p.messages = append(p.messages, map[string]interface{}{
|
||||
p.messages = append(p.messages, map[string]any{
|
||||
"role": "assistant",
|
||||
"content": assistantContent,
|
||||
})
|
||||
p.mu.Unlock()
|
||||
return renderToolCalls(toolCalls), nil
|
||||
}
|
||||
p.messages = append(p.messages, map[string]interface{}{
|
||||
p.messages = append(p.messages, map[string]any{
|
||||
"role": "assistant",
|
||||
"content": content,
|
||||
})
|
||||
@@ -347,8 +347,8 @@ func (p *openRouterProvider) Send(ctx context.Context, prompt string) (string, e
|
||||
return "", lastErr
|
||||
}
|
||||
|
||||
func buildResponsesInput(messages []map[string]interface{}) []map[string]interface{} {
|
||||
input := make([]map[string]interface{}, 0, len(messages))
|
||||
func buildResponsesInput(messages []map[string]any) []map[string]any {
|
||||
input := make([]map[string]any, 0, len(messages))
|
||||
for _, msg := range messages {
|
||||
role, _ := msg["role"].(string)
|
||||
content, _ := msg["content"].(string)
|
||||
@@ -361,10 +361,10 @@ func buildResponsesInput(messages []map[string]interface{}) []map[string]interfa
|
||||
if role == "assistant" {
|
||||
contentType = "output_text"
|
||||
}
|
||||
input = append(input, map[string]interface{}{
|
||||
input = append(input, map[string]any{
|
||||
"type": "message",
|
||||
"role": role,
|
||||
"content": []map[string]interface{}{
|
||||
"content": []map[string]any{
|
||||
{
|
||||
"type": contentType,
|
||||
"text": content,
|
||||
@@ -393,7 +393,7 @@ func buildSystemWorkingDirMessage(prompt string) string {
|
||||
return fmt.Sprintf("You are operating in this working directory: %s", wd)
|
||||
}
|
||||
|
||||
func prependSystemMessage(messages []map[string]interface{}, content string) []map[string]interface{} {
|
||||
func prependSystemMessage(messages []map[string]any, content string) []map[string]any {
|
||||
content = strings.TrimSpace(content)
|
||||
if content == "" {
|
||||
return messages
|
||||
@@ -407,8 +407,8 @@ func prependSystemMessage(messages []map[string]interface{}, content string) []m
|
||||
return messages
|
||||
}
|
||||
|
||||
out := make([]map[string]interface{}, 0, size)
|
||||
out = append(out, map[string]interface{}{
|
||||
out := make([]map[string]any, 0, size)
|
||||
out = append(out, map[string]any{
|
||||
"role": "system",
|
||||
"content": content,
|
||||
})
|
||||
@@ -436,9 +436,9 @@ func (p *openRouterProvider) Tools() []Tool {
|
||||
func (p *openRouterProvider) SetTools(tools []Tool) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.tools = make([]map[string]interface{}, len(tools))
|
||||
p.tools = make([]map[string]any, len(tools))
|
||||
for i, t := range tools {
|
||||
p.tools[i] = map[string]interface{}{
|
||||
p.tools[i] = map[string]any{
|
||||
"type": "function",
|
||||
"name": t.Name,
|
||||
"description": t.Description,
|
||||
@@ -447,7 +447,7 @@ func (p *openRouterProvider) SetTools(tools []Tool) {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *openRouterProvider) CallTool(ctx context.Context, name string, args map[string]interface{}) (string, error) {
|
||||
func (p *openRouterProvider) CallTool(ctx context.Context, name string, args map[string]any) (string, error) {
|
||||
return "", fmt.Errorf("tools not supported in openrouter provider")
|
||||
}
|
||||
|
||||
@@ -460,7 +460,7 @@ func (p *openRouterProvider) SubmitToolResult(toolCallID, result string) {
|
||||
delete(p.pendingToolCalls, toolCallID)
|
||||
|
||||
// Append tool result as a user message (tool role) for proper conversation tracking
|
||||
p.messages = append(p.messages, map[string]interface{}{
|
||||
p.messages = append(p.messages, map[string]any{
|
||||
"role": "tool",
|
||||
"tool_call_id": toolCallID,
|
||||
"name": funcName,
|
||||
@@ -489,7 +489,7 @@ func (p *openRouterProvider) TokenUsage() (int, int) {
|
||||
func (p *openRouterProvider) Reset() error {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.messages = []map[string]interface{}{}
|
||||
p.messages = []map[string]any{}
|
||||
p.pendingToolCalls = make(map[string]string)
|
||||
p.promptTokens = 0
|
||||
p.completionTokens = 0
|
||||
@@ -502,14 +502,14 @@ func (p *openRouterProvider) SetMessages(messages []Message) error {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
p.messages = make([]map[string]interface{}, 0, len(messages))
|
||||
p.messages = make([]map[string]any, 0, len(messages))
|
||||
for _, msg := range messages {
|
||||
role := strings.TrimSpace(msg.Role)
|
||||
content := msg.Content
|
||||
if role == "" || content == "" {
|
||||
continue
|
||||
}
|
||||
p.messages = append(p.messages, map[string]interface{}{
|
||||
p.messages = append(p.messages, map[string]any{
|
||||
"role": role,
|
||||
"content": content,
|
||||
})
|
||||
@@ -548,7 +548,7 @@ func (p *openRouterProvider) AppendMessages(messages []Message) error {
|
||||
if _, ok := seen[key]; ok {
|
||||
continue
|
||||
}
|
||||
p.messages = append(p.messages, map[string]interface{}{
|
||||
p.messages = append(p.messages, map[string]any{
|
||||
"role": role,
|
||||
"content": content,
|
||||
})
|
||||
@@ -595,7 +595,7 @@ func parseMaybeJSON(input string) string {
|
||||
if text == "" {
|
||||
return ""
|
||||
}
|
||||
var obj map[string]interface{}
|
||||
var obj map[string]any
|
||||
if err := json.Unmarshal([]byte(text), &obj); err == nil {
|
||||
if message, ok := obj["message"].(string); ok && strings.TrimSpace(message) != "" {
|
||||
return strings.TrimSpace(message)
|
||||
@@ -628,7 +628,7 @@ type responsesUsage struct {
|
||||
|
||||
func parseResponsesOutput(body []byte) (string, string, []responseToolCall, responsesUsage, bool) {
|
||||
var resp struct {
|
||||
Output []map[string]interface{} `json:"output"`
|
||||
Output []map[string]any `json:"output"`
|
||||
Usage struct {
|
||||
InputTokens int `json:"input_tokens"`
|
||||
OutputTokens int `json:"output_tokens"`
|
||||
@@ -649,9 +649,9 @@ func parseResponsesOutput(body []byte) (string, string, []responseToolCall, resp
|
||||
typ, _ := item["type"].(string)
|
||||
switch typ {
|
||||
case "message":
|
||||
if content, ok := item["content"].([]interface{}); ok {
|
||||
if content, ok := item["content"].([]any); ok {
|
||||
for _, c := range content {
|
||||
ci, ok := c.(map[string]interface{})
|
||||
ci, ok := c.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
@@ -686,7 +686,7 @@ func parseResponsesOutput(body []byte) (string, string, []responseToolCall, resp
|
||||
switch v := argsRaw.(type) {
|
||||
case string:
|
||||
argsStr = v
|
||||
case map[string]interface{}:
|
||||
case map[string]any:
|
||||
if b, err := json.Marshal(v); err == nil {
|
||||
argsStr = string(b)
|
||||
}
|
||||
@@ -725,18 +725,18 @@ func parseResponsesOutput(body []byte) (string, string, []responseToolCall, resp
|
||||
func renderToolCalls(calls []responseToolCall) string {
|
||||
var b strings.Builder
|
||||
for i, tc := range calls {
|
||||
call := map[string]interface{}{
|
||||
call := map[string]any{
|
||||
"name": tc.Name,
|
||||
}
|
||||
if strings.TrimSpace(tc.Arguments) != "" {
|
||||
var args map[string]interface{}
|
||||
var args map[string]any
|
||||
if err := json.Unmarshal([]byte(tc.Arguments), &args); err == nil {
|
||||
call["arguments"] = args
|
||||
} else {
|
||||
call["arguments"] = map[string]interface{}{"_raw": tc.Arguments}
|
||||
call["arguments"] = map[string]any{"_raw": tc.Arguments}
|
||||
}
|
||||
} else {
|
||||
call["arguments"] = map[string]interface{}{}
|
||||
call["arguments"] = map[string]any{}
|
||||
}
|
||||
payload, err := json.Marshal(call)
|
||||
if err != nil {
|
||||
|
||||
@@ -12,7 +12,7 @@ var DebugMode bool
|
||||
|
||||
// Tool represents an executable tool.
|
||||
type Tool struct {
|
||||
InputSchema map[string]interface{}
|
||||
InputSchema map[string]any
|
||||
Name string
|
||||
Description string
|
||||
}
|
||||
@@ -28,7 +28,7 @@ type Provider interface {
|
||||
SetThinkCallback(func(string))
|
||||
Cancel()
|
||||
Tools() []Tool
|
||||
CallTool(ctx context.Context, name string, args map[string]interface{}) (string, error)
|
||||
CallTool(ctx context.Context, name string, args map[string]any) (string, error)
|
||||
Close() error
|
||||
ContextLimit() int
|
||||
TokenUsage() (input int, output int)
|
||||
|
||||
@@ -56,13 +56,13 @@ type Scheduler struct {
|
||||
}
|
||||
|
||||
type ProviderGetter interface {
|
||||
GetProvider(cfg interface{}) (interface{}, error)
|
||||
GetProvider(cfg any) (any, error)
|
||||
}
|
||||
|
||||
type ToolServer interface {
|
||||
Name() string
|
||||
Tools() []mcp.Tool
|
||||
CallTool(ctx context.Context, name string, args map[string]interface{}) (string, error)
|
||||
CallTool(ctx context.Context, name string, args map[string]any) (string, error)
|
||||
}
|
||||
|
||||
func getDefaultDBPath() string {
|
||||
@@ -253,7 +253,7 @@ func (s *Scheduler) SetProviderGetter(pg ProviderGetter) {
|
||||
s.provider = pg
|
||||
}
|
||||
|
||||
func (s *Scheduler) SetServers(servers []interface{}) {
|
||||
func (s *Scheduler) SetServers(servers []any) {
|
||||
s.servers = nil
|
||||
for _, srv := range servers {
|
||||
ts, ok := srv.(ToolServer)
|
||||
@@ -307,20 +307,20 @@ func (s *Scheduler) generateID() (string, error) {
|
||||
}
|
||||
|
||||
type SchedulerInterface interface {
|
||||
AddTask(prompt string, intervalSec int, runAt string, providerType, providerName, providerModel string) (interface{}, error)
|
||||
AddTaskWithRepeat(prompt string, intervalSec int, runAt string, isRepeating bool, providerType, providerName, providerModel string) (interface{}, error)
|
||||
AddTask(prompt string, intervalSec int, runAt string, providerType, providerName, providerModel string) (any, error)
|
||||
AddTaskWithRepeat(prompt string, intervalSec int, runAt string, isRepeating bool, providerType, providerName, providerModel string) (any, error)
|
||||
StopTask(id string) bool
|
||||
RemoveTask(id string) bool
|
||||
ListTasks() []interface{}
|
||||
GetTask(id string) interface{}
|
||||
ListTasks() []any
|
||||
GetTask(id string) any
|
||||
}
|
||||
|
||||
func (s *Scheduler) AddTask(prompt string, intervalSec int, runAt string, providerType, providerName, providerModel string) (interface{}, error) {
|
||||
func (s *Scheduler) AddTask(prompt string, intervalSec int, runAt string, providerType, providerName, providerModel string) (any, error) {
|
||||
isRepeating := intervalSec > 0
|
||||
return s.AddTaskWithRepeat(prompt, intervalSec, runAt, isRepeating, providerType, providerName, providerModel)
|
||||
}
|
||||
|
||||
func (s *Scheduler) AddTaskWithRepeat(prompt string, intervalSec int, runAt string, isRepeating bool, providerType, providerName, providerModel string) (interface{}, error) {
|
||||
func (s *Scheduler) AddTaskWithRepeat(prompt string, intervalSec int, runAt string, isRepeating bool, providerType, providerName, providerModel string) (any, error) {
|
||||
if intervalSec <= 0 && runAt == "" {
|
||||
return nil, fmt.Errorf("must specify either interval or run_at time")
|
||||
}
|
||||
@@ -441,15 +441,15 @@ func (s *Scheduler) executeTask(task *ScheduledTask) {
|
||||
task.LastOutput = result
|
||||
}
|
||||
} else if s.provider != nil {
|
||||
var cfg interface{}
|
||||
var cfg any
|
||||
if task.ProviderType == "unknown" || task.ProviderType == "" || task.ProviderType == "current" {
|
||||
cfg = map[string]interface{}{
|
||||
cfg = map[string]any{
|
||||
"type": "current",
|
||||
"name": "current",
|
||||
"model": "current",
|
||||
}
|
||||
} else {
|
||||
cfg = map[string]interface{}{
|
||||
cfg = map[string]any{
|
||||
"type": task.ProviderType,
|
||||
"name": task.ProviderName,
|
||||
"model": task.ProviderModel,
|
||||
@@ -514,7 +514,7 @@ func (s *Scheduler) executeWithTools(ctx context.Context, task *ScheduledTask) (
|
||||
return "", fmt.Errorf("no provider available")
|
||||
}
|
||||
|
||||
cfg := map[string]interface{}{
|
||||
cfg := map[string]any{
|
||||
"type": "current",
|
||||
"name": "current",
|
||||
"model": "current",
|
||||
@@ -580,7 +580,7 @@ func (s *Scheduler) executeWithTools(ctx context.Context, task *ScheduledTask) (
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
args, ok := tc["arguments"].(map[string]interface{})
|
||||
args, ok := tc["arguments"].(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
@@ -737,7 +737,7 @@ func (s *Scheduler) looksLikeToolCall(text string) bool {
|
||||
strings.Contains(text, "function") || strings.Contains(text, "(")
|
||||
}
|
||||
|
||||
func (s *Scheduler) callTool(ctx context.Context, name string, args map[string]interface{}, timeoutSecs int) (string, error) {
|
||||
func (s *Scheduler) callTool(ctx context.Context, name string, args map[string]any, timeoutSecs int) (string, error) {
|
||||
for _, server := range s.servers {
|
||||
for _, t := range server.Tools() {
|
||||
if t.Name == name {
|
||||
@@ -837,7 +837,7 @@ func (s *Scheduler) RemoveTask(id string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Scheduler) ListTasks() []interface{} {
|
||||
func (s *Scheduler) ListTasks() []any {
|
||||
s.taskMu.RLock()
|
||||
defer s.taskMu.RUnlock()
|
||||
|
||||
@@ -848,14 +848,14 @@ func (s *Scheduler) ListTasks() []interface{} {
|
||||
sort.Slice(tasks, func(i, j int) bool {
|
||||
return tasks[i].CreatedAt.Before(tasks[j].CreatedAt)
|
||||
})
|
||||
result := make([]interface{}, len(tasks))
|
||||
result := make([]any, len(tasks))
|
||||
for i, t := range tasks {
|
||||
result[i] = t
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *Scheduler) GetTask(id string) interface{} {
|
||||
func (s *Scheduler) GetTask(id string) any {
|
||||
s.taskMu.RLock()
|
||||
defer s.taskMu.RUnlock()
|
||||
return s.tasks[id]
|
||||
|
||||
@@ -9,10 +9,10 @@ import (
|
||||
)
|
||||
|
||||
type fakeProviderGetter struct {
|
||||
p interface{}
|
||||
p any
|
||||
}
|
||||
|
||||
func (f *fakeProviderGetter) GetProvider(cfg interface{}) (interface{}, error) {
|
||||
func (f *fakeProviderGetter) GetProvider(cfg any) (any, error) {
|
||||
return f.p, nil
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ type countingToolServer struct {
|
||||
|
||||
func (c *countingToolServer) Name() string { return c.name }
|
||||
func (c *countingToolServer) Tools() []mcp.Tool { return c.tools }
|
||||
func (c *countingToolServer) CallTool(ctx context.Context, name string, args map[string]interface{}) (string, error) {
|
||||
func (c *countingToolServer) CallTool(ctx context.Context, name string, args map[string]any) (string, error) {
|
||||
c.calls++
|
||||
return "ok", nil
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ func (f *fakeToolServer) Name() string { return f.name }
|
||||
func (f *fakeToolServer) Tools() []mcp.Tool {
|
||||
return f.tools
|
||||
}
|
||||
func (f *fakeToolServer) CallTool(ctx context.Context, name string, args map[string]interface{}) (string, error) {
|
||||
func (f *fakeToolServer) CallTool(ctx context.Context, name string, args map[string]any) (string, error) {
|
||||
if name == "run_command" {
|
||||
return "ok", nil
|
||||
}
|
||||
@@ -27,7 +27,7 @@ func (f *fakeToolServer) CallTool(ctx context.Context, name string, args map[str
|
||||
|
||||
func TestScheduler_GetToolsDescription_MatchesMainFormat(t *testing.T) {
|
||||
s := &Scheduler{}
|
||||
s.SetServers([]interface{}{
|
||||
s.SetServers([]any{
|
||||
&fakeToolServer{
|
||||
name: "bash",
|
||||
tools: []mcp.Tool{
|
||||
@@ -50,7 +50,7 @@ func TestScheduler_GetToolsDescription_MatchesMainFormat(t *testing.T) {
|
||||
|
||||
func TestScheduler_CallTool_FindsTool(t *testing.T) {
|
||||
s := &Scheduler{}
|
||||
s.SetServers([]interface{}{
|
||||
s.SetServers([]any{
|
||||
&fakeToolServer{
|
||||
name: "bash",
|
||||
tools: []mcp.Tool{
|
||||
@@ -62,7 +62,7 @@ func TestScheduler_CallTool_FindsTool(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
|
||||
out, err := s.callTool(ctx, "run_command", map[string]interface{}{"command": "echo hi"}, 1)
|
||||
out, err := s.callTool(ctx, "run_command", map[string]any{"command": "echo hi"}, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("expected nil error, got %v", err)
|
||||
}
|
||||
@@ -73,7 +73,7 @@ func TestScheduler_CallTool_FindsTool(t *testing.T) {
|
||||
|
||||
func TestScheduler_CallTool_ServerNameCompatibility(t *testing.T) {
|
||||
s := &Scheduler{}
|
||||
s.SetServers([]interface{}{
|
||||
s.SetServers([]any{
|
||||
&fakeToolServer{
|
||||
name: "bash",
|
||||
tools: []mcp.Tool{
|
||||
@@ -85,7 +85,7 @@ func TestScheduler_CallTool_ServerNameCompatibility(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
|
||||
out, err := s.callTool(ctx, "Bash", map[string]interface{}{"command": "echo hi"}, 1)
|
||||
out, err := s.callTool(ctx, "Bash", map[string]any{"command": "echo hi"}, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("expected nil error, got %v", err)
|
||||
}
|
||||
|
||||
@@ -2,14 +2,15 @@ package toolcalls
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/cheikh2shift/godex/internal/rxcache"
|
||||
)
|
||||
|
||||
// ExtractAllToolCalls extracts tool calls from a model response in a variety of common formats.
|
||||
// It returns items shaped as: {"name": string, "arguments": map[string]interface{}}.
|
||||
func ExtractAllToolCalls(text string) []map[string]interface{} {
|
||||
var results []map[string]interface{}
|
||||
// It returns items shaped as: {"name": string, "arguments": map[string]any}.
|
||||
func ExtractAllToolCalls(text string) []map[string]any {
|
||||
var results []map[string]any
|
||||
|
||||
candidates := []string{text}
|
||||
if normalized := normalizeToolCallText(text); normalized != text {
|
||||
@@ -24,7 +25,7 @@ func ExtractAllToolCalls(text string) []map[string]interface{} {
|
||||
content = strings.TrimSpace(content)
|
||||
|
||||
// Try to parse the whole block as one JSON object
|
||||
var data map[string]interface{}
|
||||
var data map[string]any
|
||||
if err := json.Unmarshal([]byte(content), &data); err == nil {
|
||||
if _, ok := data["name"].(string); ok {
|
||||
results = append(results, processToolData(data))
|
||||
@@ -40,7 +41,7 @@ func ExtractAllToolCalls(text string) []map[string]interface{} {
|
||||
|
||||
// If not a single object, try sanitizing multi-line strings
|
||||
sanitized := sanitizeMultilineJson(content)
|
||||
var sanitizedData map[string]interface{}
|
||||
var sanitizedData map[string]any
|
||||
if err := json.Unmarshal([]byte(sanitized), &sanitizedData); err == nil {
|
||||
if _, ok := sanitizedData["name"].(string); ok {
|
||||
results = append(results, processToolData(sanitizedData))
|
||||
@@ -69,13 +70,13 @@ func ExtractAllToolCalls(text string) []map[string]interface{} {
|
||||
|
||||
// Safety fallback for the specific [TOOL_CALL] format
|
||||
if len(results) == 0 {
|
||||
toolCallRe := regexp.MustCompile(`(?s)\[TOOL_CALL\]\s*(.*?)\s*\[/TOOL_CALL\]`)
|
||||
toolCallRe := rxcache.MustCompile(`(?s)\[TOOL_CALL\]\s*(.*?)\s*\[/TOOL_CALL\]`)
|
||||
tcMatches := toolCallRe.FindAllStringSubmatch(candidate, -1)
|
||||
for _, m := range tcMatches {
|
||||
if len(m) > 1 {
|
||||
nameRe := regexp.MustCompile(`tool\s*=>\s*"([^"]+)"`)
|
||||
nameRe := rxcache.MustCompile(`tool\s*=>\s*"([^"]+)"`)
|
||||
if nameMatch := nameRe.FindStringSubmatch(m[1]); len(nameMatch) > 1 {
|
||||
results = append(results, map[string]interface{}{"name": nameMatch[1], "arguments": map[string]interface{}{}})
|
||||
results = append(results, map[string]any{"name": nameMatch[1], "arguments": map[string]any{}})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,11 +88,11 @@ func ExtractAllToolCalls(text string) []map[string]interface{} {
|
||||
for _, m := range nativeMatches {
|
||||
name := m[0]
|
||||
argsStr := m[1]
|
||||
var args map[string]interface{}
|
||||
var args map[string]any
|
||||
if err := json.Unmarshal([]byte(argsStr), &args); err != nil {
|
||||
args = map[string]interface{}{"_raw": argsStr}
|
||||
args = map[string]any{"_raw": argsStr}
|
||||
}
|
||||
results = append(results, map[string]interface{}{"name": name, "arguments": args})
|
||||
results = append(results, map[string]any{"name": name, "arguments": args})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,16 +104,16 @@ func ExtractAllToolCalls(text string) []map[string]interface{} {
|
||||
return results
|
||||
}
|
||||
|
||||
func extractToolCallsFromJSONArray(text string) []map[string]interface{} {
|
||||
func extractToolCallsFromJSONArray(text string) []map[string]any {
|
||||
trimmed := strings.TrimSpace(text)
|
||||
if trimmed == "" {
|
||||
return nil
|
||||
}
|
||||
var items []map[string]interface{}
|
||||
var items []map[string]any
|
||||
if err := json.Unmarshal([]byte(trimmed), &items); err != nil {
|
||||
return nil
|
||||
}
|
||||
var results []map[string]interface{}
|
||||
var results []map[string]any
|
||||
for _, item := range items {
|
||||
if item == nil {
|
||||
continue
|
||||
@@ -124,11 +125,11 @@ func extractToolCallsFromJSONArray(text string) []map[string]interface{} {
|
||||
if tool, ok := item["tool"].(string); ok {
|
||||
args := parseToolArgs(item)
|
||||
if len(args) == 0 {
|
||||
if params, ok := item["parameters"].(map[string]interface{}); ok {
|
||||
if params, ok := item["parameters"].(map[string]any); ok {
|
||||
args = params
|
||||
}
|
||||
}
|
||||
results = append(results, map[string]interface{}{
|
||||
results = append(results, map[string]any{
|
||||
"name": tool,
|
||||
"arguments": args,
|
||||
})
|
||||
@@ -139,7 +140,7 @@ func extractToolCallsFromJSONArray(text string) []map[string]interface{} {
|
||||
|
||||
func extractNativeToolCalls(text string) [][2]string {
|
||||
var results [][2]string
|
||||
re := regexp.MustCompile(`\[TOOL_CALL:\s*([^\s|]+)\s*\|`)
|
||||
re := rxcache.MustCompile(`\[TOOL_CALL:\s*([^\s|]+)\s*\|`)
|
||||
matches := re.FindAllStringSubmatchIndex(text, -1)
|
||||
for _, m := range matches {
|
||||
if len(m) < 4 {
|
||||
@@ -194,8 +195,8 @@ func normalizeToolCallText(text string) string {
|
||||
|
||||
nonEmpty := 0
|
||||
withMargin := 0
|
||||
marginRe := regexp.MustCompile(`^\s*│`)
|
||||
stripRe := regexp.MustCompile(`^\s*│\s?`)
|
||||
marginRe := rxcache.MustCompile(`^\s*│`)
|
||||
stripRe := rxcache.MustCompile(`^\s*│\s?`)
|
||||
|
||||
for _, line := range lines {
|
||||
if strings.TrimSpace(line) == "" {
|
||||
@@ -295,8 +296,8 @@ func extractCodeBlockContent(text string) []string {
|
||||
return results
|
||||
}
|
||||
|
||||
func extractJsonObjects(text string) []map[string]interface{} {
|
||||
var results []map[string]interface{}
|
||||
func extractJsonObjects(text string) []map[string]any {
|
||||
var results []map[string]any
|
||||
startIdx := -1
|
||||
braceCount := 0
|
||||
|
||||
@@ -311,7 +312,7 @@ func extractJsonObjects(text string) []map[string]interface{} {
|
||||
braceCount--
|
||||
if braceCount == 0 && startIdx != -1 {
|
||||
potentialJSON := text[startIdx : i+1]
|
||||
var data map[string]interface{}
|
||||
var data map[string]any
|
||||
if err := json.Unmarshal([]byte(potentialJSON), &data); err == nil {
|
||||
if _, ok := data["name"].(string); ok {
|
||||
results = append(results, processToolData(data))
|
||||
@@ -324,7 +325,7 @@ func extractJsonObjects(text string) []map[string]interface{} {
|
||||
return results
|
||||
}
|
||||
|
||||
func processToolData(data map[string]interface{}) map[string]interface{} {
|
||||
func processToolData(data map[string]any) map[string]any {
|
||||
name, _ := data["name"].(string)
|
||||
args := parseToolArgs(data)
|
||||
|
||||
@@ -337,33 +338,33 @@ func processToolData(data map[string]interface{}) map[string]interface{} {
|
||||
args[k] = s
|
||||
}
|
||||
}
|
||||
return map[string]interface{}{"name": name, "arguments": args}
|
||||
return map[string]any{"name": name, "arguments": args}
|
||||
}
|
||||
|
||||
func parseToolArgs(data map[string]interface{}) map[string]interface{} {
|
||||
if a, ok := data["arguments"].(map[string]interface{}); ok {
|
||||
func parseToolArgs(data map[string]any) map[string]any {
|
||||
if a, ok := data["arguments"].(map[string]any); ok {
|
||||
return a
|
||||
}
|
||||
if a, ok := data["args"].(map[string]interface{}); ok {
|
||||
if a, ok := data["args"].(map[string]any); ok {
|
||||
return a
|
||||
}
|
||||
if a, ok := data["parameters"].(map[string]interface{}); ok {
|
||||
if a, ok := data["parameters"].(map[string]any); ok {
|
||||
return a
|
||||
}
|
||||
if a, ok := data["params"].(map[string]interface{}); ok {
|
||||
if a, ok := data["params"].(map[string]any); ok {
|
||||
return a
|
||||
}
|
||||
if raw, ok := data["parameters"].(string); ok && strings.TrimSpace(raw) != "" {
|
||||
var parsed map[string]interface{}
|
||||
var parsed map[string]any
|
||||
if err := json.Unmarshal([]byte(raw), &parsed); err == nil {
|
||||
return parsed
|
||||
}
|
||||
}
|
||||
if raw, ok := data["arguments"].(string); ok && strings.TrimSpace(raw) != "" {
|
||||
var parsed map[string]interface{}
|
||||
var parsed map[string]any
|
||||
if err := json.Unmarshal([]byte(raw), &parsed); err == nil {
|
||||
return parsed
|
||||
}
|
||||
}
|
||||
return make(map[string]interface{})
|
||||
return make(map[string]any)
|
||||
}
|
||||
@@ -19,7 +19,7 @@ func TestExtractAllToolCalls_PlainJSON(t *testing.T) {
|
||||
if calls[0]["name"] != "run_command" {
|
||||
t.Fatalf("expected name=run_command, got %#v", calls[0]["name"])
|
||||
}
|
||||
args, ok := calls[0]["arguments"].(map[string]interface{})
|
||||
args, ok := calls[0]["arguments"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("expected arguments map, got %#v", calls[0]["arguments"])
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user