mirror of
https://github.com/cheikh2shift/godex.git
synced 2026-10-23 11:22:18 -05:00
Merge pull request #20 from cheikh2shift/staticcheck
Fix staticcheck issues and enhance command retrieval
This commit is contained in:
12 files changed
+217
-147
No files matched your search
+7
-4
@@ -40,8 +40,6 @@ import (
|
||||
"github.com/cheikh2shift/godex/modelquery"
|
||||
)
|
||||
|
||||
var clipboardAvailable = true
|
||||
|
||||
const (
|
||||
defaultConfigFile = "providers.yaml"
|
||||
sessionFileName = "session.txt"
|
||||
@@ -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
|
||||
}
|
||||
@@ -3191,6 +3189,11 @@ func parseToolArgs(data map[string]any) map[string]any {
|
||||
func shouldExecuteToolCall(text string) ([]map[string]any, bool) {
|
||||
toolCalls := extractAllToolCalls(text)
|
||||
if len(toolCalls) == 0 {
|
||||
|
||||
if strings.Contains(text, "<tool_code>") {
|
||||
return []map[string]any{{}}, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
return toolCalls, true
|
||||
|
||||
+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
|
||||
}
|
||||
|
||||
+19
-2
@@ -333,6 +333,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
|
||||
@@ -344,17 +346,24 @@ func (s *BashServer) isPathAllowed(path string) bool {
|
||||
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")
|
||||
|
||||
command, ok = args["cmd"].(string)
|
||||
|
||||
if !ok {
|
||||
return "", fmt.Errorf("command is required")
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -811,6 +820,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
|
||||
@@ -825,6 +836,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
|
||||
@@ -839,6 +852,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:]...)
|
||||
@@ -863,7 +878,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() {}
|
||||
|
||||
+169
-75
@@ -35,18 +35,20 @@ type MCPToolExecutor struct { //betteralign:ignore
|
||||
serverName string
|
||||
workingDir string
|
||||
|
||||
server MCPServer
|
||||
server atomic.Pointer[MCPServer]
|
||||
tools []Tool
|
||||
|
||||
mu sync.RWMutex
|
||||
requestIDMu sync.Mutex
|
||||
}
|
||||
|
||||
func NewMCPServer(ctx context.Context, server MCPServer, workingDir string) (*MCPToolExecutor, error) {
|
||||
executor := &MCPToolExecutor{
|
||||
serverName: server.Name,
|
||||
server: server,
|
||||
workingDir: workingDir,
|
||||
}
|
||||
serverCopy := server
|
||||
executor.server.Store(&serverCopy)
|
||||
|
||||
if err := executor.connect(ctx); err != nil {
|
||||
return nil, err
|
||||
@@ -56,11 +58,26 @@ 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...)
|
||||
serverPtr := m.server.Load()
|
||||
if serverPtr == nil {
|
||||
return fmt.Errorf("server not initialized")
|
||||
}
|
||||
server := *serverPtr
|
||||
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{
|
||||
@@ -74,65 +91,80 @@ 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()
|
||||
|
||||
serverPtr := m.server.Load()
|
||||
if serverPtr == nil {
|
||||
return fmt.Errorf("server not initialized")
|
||||
}
|
||||
server := *serverPtr
|
||||
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
|
||||
@@ -158,25 +190,45 @@ func listTools(ctx context.Context, mcpClient *client.Client) ([]Tool, error) {
|
||||
}
|
||||
|
||||
func (m *MCPToolExecutor) Name() string {
|
||||
return m.server.Name
|
||||
serverPtr := m.server.Load()
|
||||
if serverPtr == nil {
|
||||
return ""
|
||||
}
|
||||
return serverPtr.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
|
||||
serverPtr := m.server.Load()
|
||||
if serverPtr == nil {
|
||||
return fmt.Errorf("server not initialized")
|
||||
}
|
||||
server := *serverPtr
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -188,33 +240,53 @@ func (m *MCPToolExecutor) AddURL(ctx context.Context, url string) error {
|
||||
}
|
||||
|
||||
func (m *MCPToolExecutor) TempAddPath(path string) {
|
||||
for _, p := range m.server.AllowedPaths {
|
||||
serverPtr := m.server.Load()
|
||||
if serverPtr == nil {
|
||||
return
|
||||
}
|
||||
server := *serverPtr
|
||||
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 {
|
||||
serverPtr := m.server.Load()
|
||||
if serverPtr == nil {
|
||||
return true
|
||||
}
|
||||
server := *serverPtr
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -226,11 +298,16 @@ 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}
|
||||
serverPtr := m.server.Load()
|
||||
if serverPtr != nil {
|
||||
paths := serverPtr.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]any) (string, error) {
|
||||
@@ -257,8 +334,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)
|
||||
}
|
||||
@@ -270,7 +349,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]any) (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,
|
||||
@@ -296,7 +383,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)
|
||||
@@ -305,9 +392,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{
|
||||
@@ -369,16 +454,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 {
|
||||
@@ -399,10 +488,15 @@ func contains(slice []string, val string) bool {
|
||||
}
|
||||
|
||||
func (m *MCPToolExecutor) GetContext() string {
|
||||
serverPtr := m.server.Load()
|
||||
if serverPtr == nil {
|
||||
return ""
|
||||
}
|
||||
server := *serverPtr
|
||||
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()
|
||||
}
|
||||
@@ -513,7 +513,7 @@ func (s *FileSystemServer) replaceFirstInFile(args map[string]any) (string, erro
|
||||
|
||||
find, ok := args["find"].(string)
|
||||
if !ok || find == "" {
|
||||
return "", fmt.Errorf("find text is required")
|
||||
return "", fmt.Errorf("argument `find` is required")
|
||||
}
|
||||
|
||||
replace, ok := args["replace"].(string)
|
||||
|
||||
@@ -20,17 +20,6 @@ type schedulerExt struct {
|
||||
ListTasks() []any
|
||||
GetTask(id string) any
|
||||
}
|
||||
mu interface {
|
||||
Lock()
|
||||
Unlock()
|
||||
}
|
||||
provider interface {
|
||||
GetProvider(cfg any) (any, error)
|
||||
}
|
||||
providerMu interface {
|
||||
Lock()
|
||||
Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
type TaskInfo struct {
|
||||
|
||||
@@ -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 ...any) {
|
||||
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 {
|
||||
BlobHTTPURL string `json:"blobHTTPURL"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
func getGGUFFiles(ctx context.Context, modelID string) ([]string, error) {
|
||||
url := fmt.Sprintf("%s/models/%s", huggingfaceAPI, modelID)
|
||||
|
||||
|
||||
+15
-16
@@ -442,23 +442,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) {
|
||||
|
||||
@@ -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