Merge branch 'main' into betteralign

This commit is contained in:
Cheikh Seck authored and GitHub committed 2026-06-06 09:36:47 +00:00
commit f11968cc70
30 files changed
+264 -361

No files matched your search

+1 -1
View File
@@ -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
-5
View File
@@ -9,11 +9,6 @@ Orchestration and parallel tasks? open another terminal tab and start a new inst
![GoDex demo](screen3.gif)
## 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
View File
@@ -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
View File
@@ -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{
+2 -2
View File
@@ -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")
}
-26
View File
@@ -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
View File
@@ -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)
+1 -1
View File
@@ -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")
+1 -1
View File
@@ -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")
}
+4 -4
View File
@@ -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
View File
@@ -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)
}
+2 -2
View File
@@ -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 {
+2 -2
View File
@@ -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
View File
@@ -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")
+5 -5
View File
@@ -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,
+5 -5
View File
@@ -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")
+1 -1
View File
@@ -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...)
}
+1 -1
View File
@@ -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")
}
+1 -1
View File
@@ -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
View File
@@ -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,
})
+4 -3
View File
@@ -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
+4 -4
View File
@@ -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")
}
+7 -6
View File
@@ -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) {
+32 -32
View File
@@ -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 {
+2 -2
View File
@@ -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)
+18 -18
View File
@@ -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]
+3 -3
View File
@@ -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
}
+6 -6
View File
@@ -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)
}
+34 -33
View File
@@ -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)
}
+1 -1
View File
@@ -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"])
}