mirror of
https://github.com/cheikh2shift/godex.git
synced 2026-10-23 11:22:18 -05:00
Fix staticcheck findings
Error message case and punctuation ending is harmless, Unused code/structs helps maintenance, Unused mutexes raise questions... I've tried to fix locking for internal/mcp/client and bash, (BashServer, MCPToolExecutor) and it runs without errors compiled even with -race flag, but some comments about the concurrency access patterns would help. MCPToolExecutor is a pain point with its reconnecting logic.
This commit is contained in:
1 parent
a64e0ac7b5
commit
a2ed0d9997
14 files changed
+182
-156
No files matched your search
+4
-6
@@ -40,8 +40,6 @@ import (
|
||||
"github.com/cheikh2shift/godex/modelquery"
|
||||
)
|
||||
|
||||
var clipboardAvailable = true
|
||||
|
||||
const (
|
||||
defaultConfigFile = "providers.yaml"
|
||||
sessionFileName = "session.txt"
|
||||
@@ -1445,8 +1443,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":
|
||||
@@ -2522,7 +2520,7 @@ func callTool(ctx context.Context, servers []MCPServer, name string, args map[st
|
||||
allowedPaths := server.AllowedPaths()
|
||||
if autoDenyRestrictedPaths {
|
||||
log.Printf("[Hive] Auto-denied restricted path access: %s (allowed paths: %v)\n", path, allowedPaths)
|
||||
return "", fmt.Errorf("PATH_RESTRICTED: '%s' is not in allowed paths. Do NOT try to access this path. Find an alternative solution that does not require this file/path. If no alternative exists, respond with FINAL_ANSWER: and explain the restriction.", path)
|
||||
return "", fmt.Errorf("PATH_RESTRICTED: '%s' is not in allowed paths. Do NOT try to access this path. Find an alternative solution that does not require this file/path. If no alternative exists, respond with FINAL_ANSWER: and explain the restriction", path)
|
||||
}
|
||||
pathPromptMu.Lock()
|
||||
selected := showPathRestrictionPrompt(path, allowedPaths)
|
||||
@@ -2546,7 +2544,7 @@ func callTool(ctx context.Context, servers []MCPServer, name string, args map[st
|
||||
case 2:
|
||||
return "", ErrUserAborted
|
||||
case 3:
|
||||
return "", fmt.Errorf("PATH_RESTRICTED: '%s' is not in allowed paths. Do NOT try to access this path. Find an alternative solution that does not require this file/path. If no alternative exists, respond with FINAL_ANSWER: and explain the restriction.", path)
|
||||
return "", fmt.Errorf("PATH_RESTRICTED: '%s' is not in allowed paths. Do NOT try to access this path. Find an alternative solution that does not require this file/path. If no alternative exists, respond with FINAL_ANSWER: and explain the restriction", path)
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
+1
-3
@@ -660,9 +660,7 @@ func (m *promptModel) handleScheduleTab(value string) bool {
|
||||
return true
|
||||
}
|
||||
m.scheduleList = nil
|
||||
for _, id := range matches {
|
||||
m.scheduleList = append(m.scheduleList, id)
|
||||
}
|
||||
m.scheduleList = append(m.scheduleList, matches...)
|
||||
m.resetCompletion()
|
||||
return true
|
||||
}
|
||||
|
||||
+13
-1
@@ -332,6 +332,8 @@ func (s *BashServer) isPathAllowed(path string) bool {
|
||||
if path == "" {
|
||||
return true
|
||||
}
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
for _, allowed := range s.allowedPaths {
|
||||
if strings.HasPrefix(path, allowed) || path == allowed {
|
||||
return true
|
||||
@@ -348,12 +350,14 @@ func (s *BashServer) runCommand(ctx context.Context, args map[string]interface{}
|
||||
|
||||
if !s.isCommandAllowed(command) {
|
||||
if interpreterRegex.MatchString(command) {
|
||||
return "", fmt.Errorf("INTERPRETER_BLOCKED: scripting interpreters (python, ruby, perl, etc.) are not allowed in run_command. Use run_python or run_node instead.")
|
||||
return "", fmt.Errorf("INTERPRETER_BLOCKED: scripting interpreters (python, ruby, perl, etc.) are not allowed in run_command. Use run_python or run_node instead")
|
||||
}
|
||||
allowed, restrictedPath := s.checkCommandPaths(command)
|
||||
if !allowed && restrictedPath != "" {
|
||||
if s.autoConfirm {
|
||||
s.mu.Lock()
|
||||
s.allowedPaths = append(s.allowedPaths, restrictedPath)
|
||||
s.mu.Unlock()
|
||||
log.Printf("[BASH] Auto-confirmed restricted path: %s", restrictedPath)
|
||||
} else {
|
||||
return "", fmt.Errorf("PATH_RESTRICTED: path not allowed: %s", restrictedPath)
|
||||
@@ -810,6 +814,8 @@ func (s *BashServer) AddPath(ctx context.Context, path string) error {
|
||||
if path == "" {
|
||||
return nil
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
for _, p := range s.allowedPaths {
|
||||
if p == path {
|
||||
return nil
|
||||
@@ -824,6 +830,8 @@ func (s *BashServer) TempAddPath(path string) {
|
||||
if path == "" {
|
||||
return
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
for _, p := range s.allowedPaths {
|
||||
if p == path {
|
||||
return
|
||||
@@ -838,6 +846,8 @@ func (s *BashServer) AddURL(ctx context.Context, url string) error {
|
||||
|
||||
func (s *BashServer) RemovePath(ctx context.Context, path string) error {
|
||||
path = strings.TrimSpace(path)
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
for i, p := range s.allowedPaths {
|
||||
if p == path {
|
||||
s.allowedPaths = append(s.allowedPaths[:i], s.allowedPaths[i+1:]...)
|
||||
@@ -862,7 +872,9 @@ func (s *BashServer) Close() error {
|
||||
}
|
||||
|
||||
func (s *BashServer) SetAllowedPaths(paths []string) {
|
||||
s.mu.Lock()
|
||||
s.allowedPaths = sanitizeAllowedPaths(paths)
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *BashServer) AllowedPathsUpdated() {}
|
||||
|
||||
+142
-81
@@ -30,22 +30,21 @@ type Tool struct {
|
||||
}
|
||||
|
||||
type MCPToolExecutor struct {
|
||||
serverName string
|
||||
server MCPServer
|
||||
mcpClient *client.Client
|
||||
tools []Tool
|
||||
workingDir string
|
||||
|
||||
serverName string
|
||||
server atomic.Value
|
||||
mcpClient *client.Client
|
||||
tools []Tool
|
||||
workingDir string
|
||||
mu sync.RWMutex
|
||||
lastRequestID int64
|
||||
requestIDMu sync.Mutex
|
||||
}
|
||||
|
||||
func NewMCPServer(ctx context.Context, server MCPServer, workingDir string) (*MCPToolExecutor, error) {
|
||||
executor := &MCPToolExecutor{
|
||||
serverName: server.Name,
|
||||
server: server,
|
||||
workingDir: workingDir,
|
||||
}
|
||||
executor.server.Store(server)
|
||||
|
||||
if err := executor.connect(ctx); err != nil {
|
||||
return nil, err
|
||||
@@ -55,11 +54,22 @@ func NewMCPServer(ctx context.Context, server MCPServer, workingDir string) (*MC
|
||||
}
|
||||
|
||||
func (m *MCPToolExecutor) connect(ctx context.Context) error {
|
||||
args := m.buildArgs()
|
||||
|
||||
mcpClient, err := client.NewStdioMCPClient(m.server.Command, m.server.Env, args...)
|
||||
server := m.server.Load().(MCPServer)
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
mcpClient, tools, err := connect(ctx, server, buildArgs(server, m.workingDir))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create MCP client for %s: %w", m.server.Name, err)
|
||||
return err
|
||||
}
|
||||
m.mcpClient = mcpClient
|
||||
m.tools = tools
|
||||
return nil
|
||||
}
|
||||
|
||||
func connect(ctx context.Context, server MCPServer, args []string) (*client.Client, []Tool, error) {
|
||||
mcpClient, err := client.NewStdioMCPClient(server.Command, server.Env, args...)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to create MCP client for %s: %w", server.Name, err)
|
||||
}
|
||||
|
||||
_, err = mcpClient.Initialize(ctx, mcp.InitializeRequest{
|
||||
@@ -73,65 +83,76 @@ func (m *MCPToolExecutor) connect(ctx context.Context) error {
|
||||
})
|
||||
if err != nil {
|
||||
_ = mcpClient.Close()
|
||||
return fmt.Errorf("failed to initialize MCP client for %s: %w", m.server.Name, err)
|
||||
return nil, nil, fmt.Errorf("failed to initialize MCP client for %s: %w", server.Name, err)
|
||||
}
|
||||
|
||||
tools, err := listTools(ctx, mcpClient)
|
||||
if err != nil {
|
||||
_ = mcpClient.Close()
|
||||
return fmt.Errorf("failed to list tools for %s: %w", m.server.Name, err)
|
||||
return nil, nil, fmt.Errorf("failed to list tools for %s: %w", server.Name, err)
|
||||
}
|
||||
|
||||
m.mcpClient = mcpClient
|
||||
m.tools = tools
|
||||
return nil
|
||||
return mcpClient, tools, nil
|
||||
}
|
||||
|
||||
// ping checks if the MCP client is still responsive
|
||||
func (m *MCPToolExecutor) ping(ctx context.Context) error {
|
||||
if m.mcpClient == nil {
|
||||
return fmt.Errorf("client is nil")
|
||||
}
|
||||
|
||||
// pingClient checks if the MCP client is still responsive
|
||||
func pingClient(ctx context.Context, client client.MCPClient) error {
|
||||
// Create a context with 2 second deadline
|
||||
pingCtx, cancel := context.WithTimeout(ctx, 2*time.Second)
|
||||
defer cancel()
|
||||
|
||||
return m.mcpClient.Ping(pingCtx)
|
||||
return client.Ping(pingCtx)
|
||||
}
|
||||
|
||||
// ensureConnected pings the client and reconnects if needed
|
||||
func (m *MCPToolExecutor) ensureConnected(ctx context.Context) error {
|
||||
if m.mcpClient == nil {
|
||||
return m.connect(ctx)
|
||||
}
|
||||
|
||||
if err := m.ping(ctx); err != nil {
|
||||
// Ping failed, close and reconnect
|
||||
_ = m.mcpClient.Close()
|
||||
m.mcpClient = nil
|
||||
|
||||
if err := m.connect(ctx); err != nil {
|
||||
return fmt.Errorf("failed to reconnect after ping failure: %w", err)
|
||||
m.mu.Lock()
|
||||
client := m.mcpClient
|
||||
wd := m.workingDir
|
||||
if client != nil {
|
||||
if pingClient(ctx, client) == nil {
|
||||
m.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
// Ping failed, close and reconnect
|
||||
_ = client.Close()
|
||||
m.mcpClient = nil
|
||||
}
|
||||
// Do not hold the lock till connecting
|
||||
m.mu.Unlock()
|
||||
|
||||
server := m.server.Load().(MCPServer)
|
||||
client, tools, err := connect(ctx, server, buildArgs(server, wd))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
// Guard against another goroutine having reconnected while we were unlocked
|
||||
if m.mcpClient != nil { // assume it's ok
|
||||
_ = client.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
m.mcpClient, m.tools = client, tools
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MCPToolExecutor) buildArgs() []string {
|
||||
paths := m.server.AllowedPaths
|
||||
func buildArgs(server MCPServer, workingDir string) []string {
|
||||
paths := server.AllowedPaths
|
||||
if len(paths) == 0 {
|
||||
paths = []string{m.workingDir}
|
||||
paths = []string{workingDir}
|
||||
}
|
||||
|
||||
if m.server.Name == "filesystem" || contains(m.server.Args, "server-filesystem") {
|
||||
if server.Name == "filesystem" || contains(server.Args, "server-filesystem") {
|
||||
args := []string{"-y", "@modelcontextprotocol/server-filesystem"}
|
||||
args = append(args, paths...)
|
||||
return args
|
||||
}
|
||||
|
||||
if len(m.server.Args) > 0 {
|
||||
return m.server.Args
|
||||
if len(server.Args) > 0 {
|
||||
return server.Args
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -157,25 +178,37 @@ func listTools(ctx context.Context, mcpClient *client.Client) ([]Tool, error) {
|
||||
}
|
||||
|
||||
func (m *MCPToolExecutor) Name() string {
|
||||
return m.server.Name
|
||||
return m.server.Load().(MCPServer).Name
|
||||
}
|
||||
|
||||
func (m *MCPToolExecutor) Tools() []Tool {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return m.tools
|
||||
}
|
||||
|
||||
func (m *MCPToolExecutor) AddPath(ctx context.Context, path string) error {
|
||||
for _, p := range m.server.AllowedPaths {
|
||||
if p == path {
|
||||
return nil
|
||||
server := m.server.Load().(MCPServer)
|
||||
if func() bool {
|
||||
for _, p := range server.AllowedPaths {
|
||||
if p == path {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
server.AllowedPaths = append(server.AllowedPaths, path)
|
||||
m.server.Store(server)
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
_ = m.mcpClient.Close()
|
||||
m.mcpClient = nil
|
||||
return false
|
||||
}() {
|
||||
return nil
|
||||
}
|
||||
|
||||
m.server.AllowedPaths = append(m.server.AllowedPaths, path)
|
||||
|
||||
_ = m.mcpClient.Close()
|
||||
|
||||
if err := m.connect(ctx); err != nil {
|
||||
if err := m.ensureConnected(ctx); err != nil {
|
||||
return fmt.Errorf("failed to reconnect with new path: %w", err)
|
||||
}
|
||||
|
||||
@@ -187,33 +220,45 @@ func (m *MCPToolExecutor) AddURL(ctx context.Context, url string) error {
|
||||
}
|
||||
|
||||
func (m *MCPToolExecutor) TempAddPath(path string) {
|
||||
for _, p := range m.server.AllowedPaths {
|
||||
server := m.server.Load().(MCPServer)
|
||||
for _, p := range server.AllowedPaths {
|
||||
if p == path {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
m.server.AllowedPaths = append(m.server.AllowedPaths, path)
|
||||
server.AllowedPaths = append(server.AllowedPaths, path)
|
||||
m.server.Store(server)
|
||||
}
|
||||
|
||||
func (m *MCPToolExecutor) RemovePath(ctx context.Context, path string) error {
|
||||
found := -1
|
||||
for i, p := range m.server.AllowedPaths {
|
||||
if p == path {
|
||||
found = i
|
||||
break
|
||||
if func() bool {
|
||||
server := m.server.Load().(MCPServer)
|
||||
found := -1
|
||||
for i, p := range server.AllowedPaths {
|
||||
if p == path {
|
||||
found = i
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found == -1 {
|
||||
if found == -1 {
|
||||
return true
|
||||
}
|
||||
|
||||
server.AllowedPaths = append(server.AllowedPaths[:found], server.AllowedPaths[found+1:]...)
|
||||
m.server.Store(server)
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
_ = m.mcpClient.Close()
|
||||
m.mcpClient = nil
|
||||
return false
|
||||
}() {
|
||||
return nil
|
||||
}
|
||||
|
||||
m.server.AllowedPaths = append(m.server.AllowedPaths[:found], m.server.AllowedPaths[found+1:]...)
|
||||
|
||||
_ = m.mcpClient.Close()
|
||||
|
||||
if err := m.connect(ctx); err != nil {
|
||||
if err := m.ensureConnected(ctx); err != nil {
|
||||
return fmt.Errorf("failed to reconnect after removing path: %w", err)
|
||||
}
|
||||
|
||||
@@ -225,11 +270,14 @@ func (m *MCPToolExecutor) RemoveURL(ctx context.Context, url string) error {
|
||||
}
|
||||
|
||||
func (m *MCPToolExecutor) AllowedPaths() []string {
|
||||
paths := m.server.AllowedPaths
|
||||
if len(paths) == 0 {
|
||||
return []string{m.workingDir}
|
||||
server := m.server.Load().(MCPServer)
|
||||
paths := server.AllowedPaths
|
||||
if len(paths) != 0 {
|
||||
return paths
|
||||
}
|
||||
return paths
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return []string{m.workingDir}
|
||||
}
|
||||
|
||||
func (m *MCPToolExecutor) CallTool(ctx context.Context, name string, arguments map[string]interface{}) (string, error) {
|
||||
@@ -256,8 +304,10 @@ func (m *MCPToolExecutor) callToolWithRetry(ctx context.Context, name string, ar
|
||||
}
|
||||
|
||||
if isRetryableError(err.Error()) && attempt < maxRetries {
|
||||
m.mu.Lock()
|
||||
_ = m.mcpClient.Close()
|
||||
m.mcpClient = nil
|
||||
m.mu.Unlock()
|
||||
if reconnectErr := m.connect(ctx); reconnectErr == nil {
|
||||
return m.callToolWithRetry(ctx, name, arguments, attempt+1)
|
||||
}
|
||||
@@ -269,7 +319,15 @@ func (m *MCPToolExecutor) callToolWithRetry(ctx context.Context, name string, ar
|
||||
}
|
||||
|
||||
func (m *MCPToolExecutor) callToolWithCancellation(ctx context.Context, requestID int64, name string, arguments map[string]interface{}) (string, error) {
|
||||
t := m.mcpClient.GetTransport()
|
||||
var t transport.Interface
|
||||
m.mu.RLock()
|
||||
if client := m.mcpClient; client != nil {
|
||||
t = client.GetTransport()
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
if t == nil {
|
||||
return "", fmt.Errorf("nil client")
|
||||
}
|
||||
|
||||
request := transport.JSONRPCRequest{
|
||||
JSONRPC: mcp.JSONRPC_VERSION,
|
||||
@@ -295,7 +353,7 @@ func (m *MCPToolExecutor) callToolWithCancellation(ctx context.Context, requestI
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
m.sendCancelledNotification(context.Background(), requestID, ctx.Err().Error())
|
||||
sendCancelledNotification(context.Background(), t, requestID, ctx.Err().Error())
|
||||
return "", ctx.Err()
|
||||
case response := <-resultCh:
|
||||
return m.parseToolResponse(response)
|
||||
@@ -304,9 +362,7 @@ func (m *MCPToolExecutor) callToolWithCancellation(ctx context.Context, requestI
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MCPToolExecutor) sendCancelledNotification(ctx context.Context, requestID int64, reason string) {
|
||||
t := m.mcpClient.GetTransport()
|
||||
|
||||
func sendCancelledNotification(ctx context.Context, t transport.Interface, requestID int64, reason string) {
|
||||
notification := mcp.JSONRPCNotification{
|
||||
JSONRPC: mcp.JSONRPC_VERSION,
|
||||
Notification: mcp.Notification{
|
||||
@@ -368,16 +424,20 @@ func isRetryableError(errMsg string) bool {
|
||||
}
|
||||
|
||||
func (m *MCPToolExecutor) Close() error {
|
||||
if m.mcpClient != nil {
|
||||
return m.closeWithTimeout(2 * time.Second)
|
||||
m.mu.Lock()
|
||||
client := m.mcpClient
|
||||
m.mcpClient = nil
|
||||
m.mu.Unlock()
|
||||
if client != nil {
|
||||
return closeWithTimeout(client, 2*time.Second)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MCPToolExecutor) closeWithTimeout(timeout time.Duration) error {
|
||||
func closeWithTimeout(client *client.Client, timeout time.Duration) error {
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
done <- m.mcpClient.Close()
|
||||
done <- client.Close()
|
||||
}()
|
||||
|
||||
select {
|
||||
@@ -398,10 +458,11 @@ func contains(slice []string, val string) bool {
|
||||
}
|
||||
|
||||
func (m *MCPToolExecutor) GetContext() string {
|
||||
server := m.server.Load().(MCPServer)
|
||||
var b strings.Builder
|
||||
b.WriteString("MCP Server: " + m.server.Name + "\n")
|
||||
b.WriteString("Command: " + m.server.Command + "\n")
|
||||
b.WriteString("Args: " + strings.Join(m.server.Args, " ") + "\n")
|
||||
b.WriteString("Allowed Paths: " + strings.Join(m.server.AllowedPaths, ", ") + "\n")
|
||||
b.WriteString("MCP Server: " + server.Name + "\n")
|
||||
b.WriteString("Command: " + server.Command + "\n")
|
||||
b.WriteString("Args: " + strings.Join(server.Args, " ") + "\n")
|
||||
b.WriteString("Allowed Paths: " + strings.Join(server.AllowedPaths, ", ") + "\n")
|
||||
return b.String()
|
||||
}
|
||||
@@ -20,17 +20,6 @@ type schedulerExt struct {
|
||||
ListTasks() []interface{}
|
||||
GetTask(id string) interface{}
|
||||
}
|
||||
mu interface {
|
||||
Lock()
|
||||
Unlock()
|
||||
}
|
||||
provider interface {
|
||||
GetProvider(cfg interface{}) (interface{}, error)
|
||||
}
|
||||
providerMu interface {
|
||||
Lock()
|
||||
Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
type TaskInfo struct {
|
||||
|
||||
@@ -23,7 +23,6 @@ type cacheEntry struct {
|
||||
type WebScraperServer struct {
|
||||
allowedURLs []string
|
||||
tools []Tool
|
||||
browserCtx context.Context
|
||||
browserCancel context.CancelFunc
|
||||
autoConfirm bool
|
||||
cache map[string]cacheEntry
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var DebugMode bool
|
||||
@@ -19,12 +18,7 @@ type VisionResult struct {
|
||||
Duration float64 `json:"duration_ms"`
|
||||
}
|
||||
|
||||
var (
|
||||
visionServer *exec.Cmd
|
||||
visionServerOnce sync.Once
|
||||
visionServerErr error
|
||||
tesseractPath string
|
||||
)
|
||||
var visionServer *exec.Cmd
|
||||
|
||||
func visionDebug(format string, args ...interface{}) {
|
||||
if DebugMode {
|
||||
@@ -185,7 +179,6 @@ func StopVisionServer() {
|
||||
if visionServer != nil && visionServer.Process != nil {
|
||||
visionServer.Process.Signal(os.Kill)
|
||||
visionServer = nil
|
||||
visionServerOnce = sync.Once{}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,11 +19,6 @@ type hfRepoInfo struct {
|
||||
} `json:"siblings"`
|
||||
}
|
||||
|
||||
type hfFileInfo struct {
|
||||
Size int64 `json:"size"`
|
||||
BlobHTTPURL string `json:"blobHTTPURL"`
|
||||
}
|
||||
|
||||
func getGGUFFiles(ctx context.Context, modelID string) ([]string, error) {
|
||||
url := fmt.Sprintf("%s/models/%s", huggingfaceAPI, modelID)
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ func (g *geminiProvider) Cancel() {
|
||||
}
|
||||
|
||||
func (g *geminiProvider) CallTool(ctx context.Context, name string, args map[string]interface{}) (string, error) {
|
||||
return "", fmt.Errorf("Gemini provider does not support direct tool calls")
|
||||
return "", fmt.Errorf("gemini provider does not support direct tool calls")
|
||||
}
|
||||
|
||||
func (g *geminiProvider) ContextLimit() int {
|
||||
|
||||
+15
-16
@@ -441,23 +441,22 @@ func promptQuantizationSelection(maxOptions int, defaultIdx int) (int, error) {
|
||||
return 0, fmt.Errorf("no quantization options available")
|
||||
}
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
for {
|
||||
fmt.Printf("\nSelect quantization (default: %d for Q4_K_M): ", defaultIdx+1)
|
||||
input, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
input = strings.TrimSpace(input)
|
||||
if input == "" {
|
||||
return defaultIdx, nil
|
||||
}
|
||||
idx, err := strconv.Atoi(input)
|
||||
if err != nil || idx < 1 || idx > maxOptions {
|
||||
fmt.Println("Invalid selection, using default Q4_K_M")
|
||||
return defaultIdx, nil
|
||||
}
|
||||
return idx - 1, nil
|
||||
|
||||
fmt.Printf("\nSelect quantization (default: %d for Q4_K_M): ", defaultIdx+1)
|
||||
input, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
input = strings.TrimSpace(input)
|
||||
if input == "" {
|
||||
return defaultIdx, nil
|
||||
}
|
||||
idx, err := strconv.Atoi(input)
|
||||
if err != nil || idx < 1 || idx > maxOptions {
|
||||
fmt.Println("Invalid selection, using default Q4_K_M")
|
||||
return defaultIdx, nil
|
||||
}
|
||||
return idx - 1, nil
|
||||
}
|
||||
|
||||
func getCachedQuantization(modelID string) (string, bool) {
|
||||
|
||||
@@ -476,7 +476,7 @@ func (o *ollamaProvider) Cancel() {
|
||||
}
|
||||
|
||||
func (o *ollamaProvider) CallTool(ctx context.Context, name string, args map[string]interface{}) (string, error) {
|
||||
return "", fmt.Errorf("Ollama provider does not support direct tool calls, use MCP servers configured in provider")
|
||||
return "", fmt.Errorf("ollama provider does not support direct tool calls, use MCP servers configured in provider")
|
||||
}
|
||||
|
||||
func (o *ollamaProvider) ContextLimit() int {
|
||||
|
||||
@@ -44,15 +44,6 @@ type openRouterModelsResponse struct {
|
||||
Data []openRouterModelInfo `json:"data"`
|
||||
}
|
||||
|
||||
type toolCall struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Function struct {
|
||||
Name string `json:"name"`
|
||||
Arguments string `json:"arguments"`
|
||||
} `json:"function"`
|
||||
}
|
||||
|
||||
type responseToolCall struct {
|
||||
ID string
|
||||
Name string
|
||||
@@ -681,7 +672,7 @@ func parseResponsesOutput(body []byte) (string, string, []responseToolCall, resp
|
||||
case "function_call":
|
||||
name, _ := item["name"].(string)
|
||||
id, _ := item["id"].(string)
|
||||
argsRaw, _ := item["arguments"]
|
||||
argsRaw := item["arguments"]
|
||||
argsStr := ""
|
||||
switch v := argsRaw.(type) {
|
||||
case string:
|
||||
|
||||
@@ -365,7 +365,7 @@ func (s *Scheduler) saveTask(task *ScheduledTask) error {
|
||||
lastRunStr = task.LastRun.Format(time.RFC3339)
|
||||
}
|
||||
_, err := s.db.Exec(`
|
||||
INSERT OR REPLACE INTO scheduled_tasks
|
||||
INSERT OR REPLACE INTO scheduled_tasks
|
||||
(id, prompt, interval_sec, run_at, is_repeating, working_dir, created_at, last_run, run_count, last_error, last_output, provider_type, provider_name, provider_model)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`, task.ID, task.Prompt, task.IntervalSec, task.RunAt, task.IsRepeating, task.WorkingDir,
|
||||
@@ -867,9 +867,3 @@ func (s *Scheduler) Close() {
|
||||
s.wg.Wait()
|
||||
s.db.Close()
|
||||
}
|
||||
|
||||
type providerConfig struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Model string `json:"model"`
|
||||
}
|
||||
@@ -59,7 +59,7 @@ func RunWizard(destination string) error {
|
||||
return defaultVal
|
||||
}
|
||||
if existingProvider.Type != "" && provider.Type != "" &&
|
||||
strings.ToLower(existingProvider.Type) != strings.ToLower(provider.Type) {
|
||||
!strings.EqualFold(existingProvider.Type, provider.Type) {
|
||||
switch field {
|
||||
case "model", "description", "endpoint", "api_key_env":
|
||||
return defaultVal
|
||||
@@ -337,10 +337,7 @@ func looksLikePromptEcho(input string) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if strings.Contains(input, "]:") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return strings.Contains(input, "]:")
|
||||
}
|
||||
|
||||
type selectOption struct {
|
||||
|
||||
Reference in new issue
Block a user