diff options
| author | Kujtim Hoxha <[email protected]> | 2025-04-19 15:15:29 +0200 |
|---|---|---|
| committer | Kujtim Hoxha <[email protected]> | 2025-04-21 13:42:29 +0200 |
| commit | 2b5a33e476ae3c6b5c6345777d20792786836dda (patch) | |
| tree | ca22c0a7d6c8f73a21e2b67f31b927b7df187d4d /internal/llm | |
| parent | bf8cd3bd128d97cf93fcd1481c37db0e46945fd2 (diff) | |
| download | opencode-2b5a33e476ae3c6b5c6345777d20792786836dda.tar.gz opencode-2b5a33e476ae3c6b5c6345777d20792786836dda.zip | |
lsp improvements
Diffstat (limited to 'internal/llm')
| -rw-r--r-- | internal/llm/models/gemini.go | 63 | ||||
| -rw-r--r-- | internal/llm/models/models.go | 5 | ||||
| -rw-r--r-- | internal/llm/prompt/coder.go | 2 | ||||
| -rw-r--r-- | internal/llm/provider/gemini.go | 1 | ||||
| -rw-r--r-- | internal/llm/tools/grep.go | 91 |
5 files changed, 124 insertions, 38 deletions
diff --git a/internal/llm/models/gemini.go b/internal/llm/models/gemini.go new file mode 100644 index 000000000..00bf7387f --- /dev/null +++ b/internal/llm/models/gemini.go @@ -0,0 +1,63 @@ +package models + +const ( + ProviderGemini ModelProvider = "gemini" + + // Models + Gemini25Flash ModelID = "gemini-2.5-flash" + Gemini25 ModelID = "gemini-2.5" + Gemini20Flash ModelID = "gemini-2.0-flash" + Gemini20FlashLite ModelID = "gemini-2.0-flash-lite" +) + +var GeminiModels = map[ModelID]Model{ + Gemini25Flash: { + ID: Gemini25Flash, + Name: "Gemini 2.5 Flash", + Provider: ProviderGemini, + APIModel: "gemini-2.5-flash-preview-04-17", + CostPer1MIn: 0.15, + CostPer1MInCached: 0, + CostPer1MOutCached: 0, + CostPer1MOut: 0.60, + ContextWindow: 1000000, + DefaultMaxTokens: 50000, + }, + Gemini25: { + ID: Gemini25, + Name: "Gemini 2.5 Pro", + Provider: ProviderGemini, + APIModel: "gemini-2.5-pro-preview-03-25", + CostPer1MIn: 1.25, + CostPer1MInCached: 0, + CostPer1MOutCached: 0, + CostPer1MOut: 10, + ContextWindow: 1000000, + DefaultMaxTokens: 50000, + }, + + Gemini20Flash: { + ID: Gemini20Flash, + Name: "Gemini 2.0 Flash", + Provider: ProviderGemini, + APIModel: "gemini-2.0-flash", + CostPer1MIn: 0.10, + CostPer1MInCached: 0, + CostPer1MOutCached: 0, + CostPer1MOut: 0.40, + ContextWindow: 1000000, + DefaultMaxTokens: 6000, + }, + Gemini20FlashLite: { + ID: Gemini20FlashLite, + Name: "Gemini 2.0 Flash Lite", + Provider: ProviderGemini, + APIModel: "gemini-2.0-flash-lite", + CostPer1MIn: 0.05, + CostPer1MInCached: 0, + CostPer1MOutCached: 0, + CostPer1MOut: 0.30, + ContextWindow: 1000000, + DefaultMaxTokens: 6000, + }, +} diff --git a/internal/llm/models/models.go b/internal/llm/models/models.go index aba4a10c3..cccbd2765 100644 --- a/internal/llm/models/models.go +++ b/internal/llm/models/models.go @@ -23,9 +23,6 @@ type Model struct { // Model IDs const ( // GEMINI - GEMINI25 ModelID = "gemini-2.5" - GRMINI20Flash ModelID = "gemini-2.0-flash" - // GROQ QWENQwq ModelID = "qwen-qwq" @@ -35,7 +32,6 @@ const ( // GEMINI const ( ProviderBedrock ModelProvider = "bedrock" - ProviderGemini ModelProvider = "gemini" ProviderGROQ ModelProvider = "groq" // ForTests @@ -95,4 +91,5 @@ var SupportedModels = map[ModelID]Model{ func init() { maps.Copy(SupportedModels, AnthropicModels) maps.Copy(SupportedModels, OpenAIModels) + maps.Copy(SupportedModels, GeminiModels) } diff --git a/internal/llm/prompt/coder.go b/internal/llm/prompt/coder.go index d7ca7b2fd..cc0da0313 100644 --- a/internal/llm/prompt/coder.go +++ b/internal/llm/prompt/coder.go @@ -68,6 +68,7 @@ You MUST adhere to the following criteria when executing the task: - Do NOT show the full contents of large files you have already written, unless the user explicitly asks for them. - When doing things with paths, always use use the full path, if the working directory is /abc/xyz and you want to edit the file abc.go in the working dir refer to it as /abc/xyz/abc.go. - If you send a path not including the working dir, the working dir will be prepended to it. +- Remember the user does not see the full output of tools ` const baseAnthropicCoderPrompt = `You are OpenCode, an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user. @@ -162,6 +163,7 @@ NEVER commit changes unless the user explicitly asks you to. It is VERY IMPORTAN # Tool usage policy - When doing file search, prefer to use the Agent tool in order to reduce context usage. - If you intend to call multiple tools and there are no dependencies between the calls, make all of the independent calls in the same function_calls block. +- IMPORTANT: The user does not see the full output of the tool responses, so if you need the output of the tool for the response make sure to summarize it for the user. You MUST answer concisely with fewer than 4 lines of text (not including tool use or code generation), unless user asks for detail.` diff --git a/internal/llm/provider/gemini.go b/internal/llm/provider/gemini.go index 384bff900..a5e6ed877 100644 --- a/internal/llm/provider/gemini.go +++ b/internal/llm/provider/gemini.go @@ -567,4 +567,3 @@ func contains(s string, substrs ...string) bool { } return false } - diff --git a/internal/llm/tools/grep.go b/internal/llm/tools/grep.go index 086a5e686..475370ffb 100644 --- a/internal/llm/tools/grep.go +++ b/internal/llm/tools/grep.go @@ -10,6 +10,7 @@ import ( "path/filepath" "regexp" "sort" + "strconv" "strings" "time" @@ -24,8 +25,10 @@ type GrepParams struct { } type grepMatch struct { - path string - modTime time.Time + path string + modTime time.Time + lineNum int + lineText string } type GrepResponseMetadata struct { @@ -147,13 +150,26 @@ func (g *grepTool) Run(ctx context.Context, call ToolCall) (ToolResponse, error) if len(matches) == 0 { output = "No files found" } else { - output = fmt.Sprintf("Found %d file%s\n%s", - len(matches), - pluralize(len(matches)), - strings.Join(matches, "\n")) + output = fmt.Sprintf("Found %d matches\n", len(matches)) + + currentFile := "" + for _, match := range matches { + if currentFile != match.path { + if currentFile != "" { + output += "\n" + } + currentFile = match.path + output += fmt.Sprintf("%s:\n", match.path) + } + if match.lineNum > 0 { + output += fmt.Sprintf(" Line %d: %s\n", match.lineNum, match.lineText) + } else { + output += fmt.Sprintf(" %s\n", match.path) + } + } if truncated { - output += "\n\n(Results are truncated. Consider using a more specific path or pattern.)" + output += "\n(Results are truncated. Consider using a more specific path or pattern.)" } } @@ -166,14 +182,7 @@ func (g *grepTool) Run(ctx context.Context, call ToolCall) (ToolResponse, error) ), nil } -func pluralize(count int) string { - if count == 1 { - return "" - } - return "s" -} - -func searchFiles(pattern, rootPath, include string, limit int) ([]string, bool, error) { +func searchFiles(pattern, rootPath, include string, limit int) ([]grepMatch, bool, error) { matches, err := searchWithRipgrep(pattern, rootPath, include) if err != nil { matches, err = searchFilesWithRegex(pattern, rootPath, include) @@ -191,12 +200,7 @@ func searchFiles(pattern, rootPath, include string, limit int) ([]string, bool, matches = matches[:limit] } - results := make([]string, len(matches)) - for i, m := range matches { - results[i] = m.path - } - - return results, truncated, nil + return matches, truncated, nil } func searchWithRipgrep(pattern, path, include string) ([]grepMatch, error) { @@ -205,7 +209,8 @@ func searchWithRipgrep(pattern, path, include string) ([]grepMatch, error) { return nil, fmt.Errorf("ripgrep not found: %w", err) } - args := []string{"-l", pattern} + // Use -n to show line numbers and include the matched line + args := []string{"-n", pattern} if include != "" { args = append(args, "--glob", include) } @@ -228,14 +233,29 @@ func searchWithRipgrep(pattern, path, include string) ([]grepMatch, error) { continue } - fileInfo, err := os.Stat(line) + // Parse ripgrep output format: file:line:content + parts := strings.SplitN(line, ":", 3) + if len(parts) < 3 { + continue + } + + filePath := parts[0] + lineNum, err := strconv.Atoi(parts[1]) + if err != nil { + continue + } + lineText := parts[2] + + fileInfo, err := os.Stat(filePath) if err != nil { continue // Skip files we can't access } matches = append(matches, grepMatch{ - path: line, - modTime: fileInfo.ModTime(), + path: filePath, + modTime: fileInfo.ModTime(), + lineNum: lineNum, + lineText: lineText, }) } @@ -276,15 +296,17 @@ func searchFilesWithRegex(pattern, rootPath, include string) ([]grepMatch, error return nil } - match, err := fileContainsPattern(path, regex) + match, lineNum, lineText, err := fileContainsPattern(path, regex) if err != nil { return nil // Skip files we can't read } if match { matches = append(matches, grepMatch{ - path: path, - modTime: info.ModTime(), + path: path, + modTime: info.ModTime(), + lineNum: lineNum, + lineText: lineText, }) if len(matches) >= 200 { @@ -301,21 +323,24 @@ func searchFilesWithRegex(pattern, rootPath, include string) ([]grepMatch, error return matches, nil } -func fileContainsPattern(filePath string, pattern *regexp.Regexp) (bool, error) { +func fileContainsPattern(filePath string, pattern *regexp.Regexp) (bool, int, string, error) { file, err := os.Open(filePath) if err != nil { - return false, err + return false, 0, "", err } defer file.Close() scanner := bufio.NewScanner(file) + lineNum := 0 for scanner.Scan() { - if pattern.MatchString(scanner.Text()) { - return true, nil + lineNum++ + line := scanner.Text() + if pattern.MatchString(line) { + return true, lineNum, line, nil } } - return false, scanner.Err() + return false, 0, "", scanner.Err() } func globToRegex(glob string) string { |
