Skip to content

Commit

Permalink
Merge pull request #52 from meysamhadeli/fix/fix-bug-help-subcommand-…
Browse files Browse the repository at this point in the history
…code

fix: fix bug in subcommand code for help
  • Loading branch information
meysamhadeli authored Nov 10, 2024
2 parents 3a5e43c + 1b63cf8 commit 6a5d9e5
Show file tree
Hide file tree
Showing 10 changed files with 44 additions and 65 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

> 💡 **codai is an AI code assistant designed to help developers efficiently manage their daily tasks through a session-based CLI, such as adding new features, refactoring,
and performing detailed code reviews. What makes codai stand out is its deep understanding of the entire context of your project, enabling it to analyze your code base
and suggest improvements or new code based on your context. This AI-powered tool supports multiple LLM models, including GPT-4o, GPT-4, GPT-4o mini, Ollama, and more.**
and suggest improvements or new code based on your context. This AI-powered tool supports multiple LLM models, including GPT-4o, GPT-4, Ollama, and more.**

We use **two** main methods to manage context: **RAG** (Retrieval-Augmented Generation) and **Summarize Full Context of Code**.
Each method has its own benefits and is chosen depending on the specific needs of the request. Below is a description of each method.
Expand Down Expand Up @@ -133,7 +133,7 @@ Summarize the full context of your codebase using Tree-sitter for accurate and e
Implement a Retrieval-Augmented Generation system to improve the relevance and accuracy of code suggestions by retrieving relevant context from the project.

⚡ **Support variety of LLM models:**
Work with advanced LLM models like `GPT-4o, GPT-4, GPT-4o mini and Ollama` to get high-quality code suggestions and interactions.
Work with advanced LLM models like `GPT-4o, GPT-4 and Ollama` to get high-quality code suggestions and interactions.

🗂️ **Edit Multiple Files at Once:**
Enable the AI to modify several files at the same time, making it easier to handle complex requests that need changes in different areas of the code.
Expand Down
45 changes: 26 additions & 19 deletions cmd/code.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,11 @@ based on the current project context. Each interaction is part of a session, all
improved responses throughout the user experience.`,
Run: func(cmd *cobra.Command, args []string) {
rootDependencies := handleRootCommand(cmd)
err := handleCodeCommand(rootDependencies)
if err != nil {
fmt.Println(lipgloss.Red.Render(fmt.Sprintf("%v", err)))
rootDependencies.TokenManagement.DisplayTokens(rootDependencies.Config.AIProviderConfig.ProviderName, rootDependencies.Config.AIProviderConfig.ChatCompletionModel, rootDependencies.Config.AIProviderConfig.EmbeddingModel, rootDependencies.Config.RAG)
}
handleCodeCommand(rootDependencies)
},
}

func handleCodeCommand(rootDependencies *RootDependencies) error {
func handleCodeCommand(rootDependencies *RootDependencies) {

// Create a context with cancel function
ctx, cancel := context.WithCancel(context.Background())
Expand All @@ -59,30 +55,35 @@ func handleCodeCommand(rootDependencies *RootDependencies) error {

var fullContextCodes []string

codeOptionsBox := lipgloss.BoxStyle.Render(":help Help for code subcommand")
fmt.Println(codeOptionsBox)

spinnerLoadContext, err := pterm.DefaultSpinner.WithStyle(pterm.NewStyle(pterm.FgLightBlue)).WithSequence("⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏").WithDelay(100).Start("Loading Context...")

// Get all data files from the root directory
fullContextFiles, fullContextCodes, err = rootDependencies.Analyzer.GetProjectFiles(rootDependencies.Cwd)

if err != nil {
spinnerLoadContext.Stop()
return fmt.Errorf(lipgloss.Red.Render(fmt.Sprintf("%v", err)))
fmt.Println(lipgloss.Red.Render(fmt.Sprintf("%v", err)))
}

spinnerLoadContext.Stop()

codeOptionsBox := lipgloss.BoxStyle.Render(":help Help for code subcommand")
fmt.Println(codeOptionsBox)

// Launch the user input handler in a goroutine
startLoop: // Label for the start loop
for {
select {
case <-ctx.Done():
<-done // Wait for gracefulShutdown to complete
return nil
return

default:

displayTokens := func() {
rootDependencies.TokenManagement.DisplayTokens(rootDependencies.Config.AIProviderConfig.ProviderName, rootDependencies.Config.AIProviderConfig.ChatCompletionModel, rootDependencies.Config.AIProviderConfig.EmbeddingModel, rootDependencies.Config.RAG)
}

// Get user input
userInput, err := utils.InputPrompt(reader)
if err != nil {
Expand All @@ -91,16 +92,16 @@ startLoop: // Label for the start loop
}

// Configure help code subcommand
shouldContinue, shouldSkip := findCodeSubCommand(userInput, rootDependencies)
isHelpSubcommands, exit := findCodeSubCommand(userInput, rootDependencies)

if shouldContinue {
if isHelpSubcommands {
continue
}

if shouldSkip {
if exit {
cancel() // Initiate shutdown for the app's own ":exit" command
<-done // Wait for gracefulShutdown to complete
return nil
return
}

// If RAG is enabled, we use RAG system for retrieve most relevant data due user request
Expand Down Expand Up @@ -138,6 +139,7 @@ startLoop: // Label for the start loop
for err = range errorChan {
spinnerLoadContextEmbedding.Stop()
fmt.Println(lipgloss.Red.Render(fmt.Sprintf("%v", err)))
displayTokens()
continue startLoop
}

Expand Down Expand Up @@ -167,6 +169,7 @@ startLoop: // Label for the start loop
if err != nil {
spinnerLoadContextEmbedding.Stop()
fmt.Println(lipgloss.Red.Render(fmt.Sprintf("%v", err)))
displayTokens()
continue startLoop
}

Expand All @@ -179,6 +182,8 @@ startLoop: // Label for the start loop
finalPrompt, userInputPrompt := rootDependencies.Analyzer.GeneratePrompt(fullContextCodes, rootDependencies.ChatHistory.GetHistory(), userInput, requestedContext)

// Step 7: Send the relevant code and user input to the AI API
var b = finalPrompt + userInputPrompt
fmt.Println(b)
responseChan := rootDependencies.CurrentProvider.ChatCompletionRequest(ctx, userInputPrompt, finalPrompt)

// Iterate over response channel to handle streamed data or errors.
Expand Down Expand Up @@ -208,6 +213,7 @@ startLoop: // Label for the start loop

if err != nil {
fmt.Println(lipgloss.Red.Render(fmt.Sprintf("%v", err)))
displayTokens()
continue startLoop
}

Expand All @@ -224,6 +230,7 @@ startLoop: // Label for the start loop

if err != nil {
fmt.Println(lipgloss.Red.Render(fmt.Sprintf("%v", err)))
displayTokens()
continue
}
}
Expand All @@ -234,6 +241,7 @@ startLoop: // Label for the start loop

if changes == nil {
fmt.Println(lipgloss.BlueSky.Render("\nno code blocks with a valid path detected to apply."))
displayTokens()
continue
}

Expand Down Expand Up @@ -278,8 +286,7 @@ startLoop: // Label for the start loop
}
spinnerUpdateContext.Stop()
}

rootDependencies.TokenManagement.DisplayTokens(rootDependencies.Config.AIProviderConfig.ProviderName, rootDependencies.Config.AIProviderConfig.ChatCompletionModel, rootDependencies.Config.AIProviderConfig.EmbeddingModel, rootDependencies.Config.RAG)
displayTokens()
}
}
}
Expand All @@ -295,7 +302,7 @@ func findCodeSubCommand(command string, rootDependencies *RootDependencies) (boo
fmt.Print("\033[2J\033[H")
return true, false
case ":exit":
return false, true
return false, false
case ":token":
rootDependencies.TokenManagement.DisplayTokens(
rootDependencies.Config.AIProviderConfig.ProviderName,
Expand All @@ -311,6 +318,6 @@ func findCodeSubCommand(command string, rootDependencies *RootDependencies) (boo
rootDependencies.ChatHistory.ClearHistory()
return true, false
default:
return true, false
return false, false
}
}
4 changes: 2 additions & 2 deletions code_analyzer/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func (analyzer *CodeAnalyzer) GetProjectFiles(rootDir string) ([]models.FileData

// Append the file data to the result
result = append(result, models.FileData{RelativePath: relativePath, Code: string(content), TreeSitterCode: strings.Join(codeParts, "\n")})
codes = append(codes, fmt.Sprintf("File: %s\n\n%s", relativePath, strings.Join(codeParts, "\n")))
codes = append(codes, fmt.Sprintf("**File: %s**\n\n%s", relativePath, strings.Join(codeParts, "\n")))
}

return nil
Expand Down Expand Up @@ -251,7 +251,7 @@ func (analyzer *CodeAnalyzer) ExtractCodeChanges(text string) []models.CodeChang
}

// Regex patterns for file paths and code blocks
filePathPattern := regexp.MustCompile(`(?i)(?:\d+\.\s*|File:\s*)([^\s*]+?\.[a-zA-Z0-9]+)\b`)
filePathPattern := regexp.MustCompile("(?i)(?:\\d+\\.\\s*|File:\\s*)[`']?([^\\s*`']+?\\.[a-zA-Z0-9]+)[`']?\\b")
// Capture entire diff blocks, assuming they are enclosed in ```diff ... ```
codeBlockPattern := regexp.MustCompile("(?s)```[a-zA-Z0-9]*\\s*(.*?)\\s*```")

Expand Down
11 changes: 5 additions & 6 deletions embed_data/prompts/rag_context_prompt.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- You always COMPLETELY IMPLEMENT the needed code!
- Explain any needed changes.


## General Instructions for Code Modifications:
- If you **modify** or **add** lines in code, follow the exact format for the **Code BLOCK**:
- **First line**: the **file name** with **relative path**; no extra markup, punctuation, comments, etc. **JUST** the **file name** with **relative path** and **file name** should using **naming conversion** base on **language**.
Expand All @@ -20,11 +21,10 @@
- Prefix **"-"** for removed lines.
- **Unchanged lines** should **not** be prefixed; they should remain as they are.
- **Last line**: End of the markdown highlighted **Code BLOCK**.
- If I have multiple files, **split** them to seperated **Code BLOCK**.
- Always add **relative path** and **file name** **top** of each Code BLOCK.
- If you provide a **diff** always give me full code and **do not summarize that** only for **modify part** and I want also **all of unchanged line**.

## Code BLOCK Format:
- Every code modification **must follow** this pattern strictly, ensuring that added, removed, and unchanged lines are marked correctly with **"+"**, **"-"**, or skipped for unchanged lines.
- Every code modification **must follow** this pattern strictly, ensuring that added, removed, and unchanged lines are marked correctly with **"+"**, **"-"**, or skipped for unchanged lines and keep in mind **give me all of unchanged line and dont summarize them.**
- **Example format**:

File: relativePath/fileName.ext
Expand Down Expand Up @@ -53,9 +53,8 @@ File: relativePath/fileName.ext
- Any lines that are deleted **must be prefixed with a minus sign (-)**.

## For Unchanged Lines:
- Always give me unchanged line **without summarize** them **exactly like as they were before**.
- Any line of code that remains unchanged **should not be prefixed and should remain as is**.

## Important:
- Always add **relative path** and **file name** **top** of each Code BLOCK.
- If you create new files as part of the solution, **always include the relative path and file name** of the new file with your changes.
- Under no circumstances, if the Code BLOCK is empty or Code BLOCK is incomplete, do **not** include placeholder comments like "// REST OF THE CODE" or "// IMPLEMENTATION OF....".
- Under no circumstances, if the Code BLOCK is empty or Code BLOCK is incomplete, do **not** include placeholder comments like "// REST OF THE CODE" or "// IMPLEMENTATION OF....".
13 changes: 6 additions & 7 deletions embed_data/prompts/summarize_full_context_prompt.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,14 @@
- Prefix **"-"** for removed lines.
- **Unchanged lines** should **not** be prefixed; they should remain as they are.
- **Last line**: End of the markdown highlighted **Code BLOCK**.
- If I have multiple files, **split** them to seperated **Code BLOCK**.
- Always add **relative path** and **file name** **top** of each Code BLOCK.
- If you provide a **diff** always give me full code and **do not summarize that** only for **modify part** and I want also **all of unchanged line**.

## Code BLOCK Format:
- Every code modification **must follow** this pattern strictly, ensuring that added, removed, and unchanged lines are marked correctly with **"+"**, **"-"**, or skipped for unchanged lines.
- Every code modification **must follow** this pattern strictly, ensuring that added, removed, and unchanged lines are marked correctly with **"+"**, **"-"**, or skipped for unchanged lines and keep in mind **give me all of unchanged line and dont summarize them.**
- **Example format**:

File: relativePath/fileName.ext
diff
```diff
package main

import "fmt"
Expand All @@ -62,7 +61,8 @@ diff
+ fmt.Println("Current time:", time.Now())
fmt.Println("This is another unchanged line")
}
- The **added lines** would have the **"+"** prefix.
```
- The **added lines** would have the **"+"** prefix.
- The **removed lines** would have the **"-"** prefix.
- The **unchanged lines** should **remain as is** without any prefix.

Expand All @@ -74,9 +74,8 @@ diff
- Any lines that are deleted **must be prefixed with a minus sign (-)**.

## For Unchanged Lines:
- Always give me unchanged line **without summarize** them **exactly like as they were before**.
- Any line of code that remains unchanged **should not be prefixed and should remain as is**.

## Important:
- Always add **relative path** and **file name** **top** of each Code BLOCK.
- If you create new files as part of the solution, **always include the relative path and file name** of the new file with your changes.
- Under no circumstances, if the Code BLOCK is empty or Code BLOCK is incomplete, do **not** include placeholder comments like "// REST OF THE CODE" or "// IMPLEMENTATION OF....".
2 changes: 1 addition & 1 deletion embedding_store/embedding_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (store *EmbeddingStore) FindRelevantChunks(queryEmbedding []float64, topN i
var relevantCode []string
for i := 0; i < len(results) && (topN == -1 || i < topN); i++ {
fileName := results[i].FileName
relevantCode = append(relevantCode, fmt.Sprintf("File: %s\nSimilarity: %.4f\n%s", fileName, results[i].Similarity, store.CodeStore[fileName]))
relevantCode = append(relevantCode, fmt.Sprintf("**File: %s**\n\n%s", fileName, store.CodeStore[fileName]))
}

return relevantCode
Expand Down
2 changes: 1 addition & 1 deletion providers/chat_history.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func (ch *chatHistory) GetHistory() []string {
// AddToHistory Method to add conversation to the session history
func (ch *chatHistory) AddToHistory(userInputPrompt string, aiResponse string) {

history := fmt.Sprintf(fmt.Sprintf("### History time:\n\n%v\n---------\n\n", time.Now())+"## Here is user request:\n\n%s\n---------\n\n## Here is the the response from using AI:\n\n%s", userInputPrompt, aiResponse)
history := fmt.Sprintf(fmt.Sprintf("### History time:\n\n%v\n---------\n\n", time.Now())+"### Here is user request:\n\n%s\n---------\n\n### Here is the the response from using AI:\n\n%s", userInputPrompt, aiResponse)

ch.History = append(ch.History, history)
}
Expand Down
14 changes: 0 additions & 14 deletions tests/fakes/Foo.cs

This file was deleted.

12 changes: 0 additions & 12 deletions tests/fakes/Foo1.cs

This file was deleted.

2 changes: 1 addition & 1 deletion utils/confirm_prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func ConfirmPrompt(path string) (bool, error) {
reader := bufio.NewReader(os.Stdin)

// Styled prompt message
fmt.Printf(lipgloss.BlueSky.Render(fmt.Sprintf("Do you want to accept the change for file '%v'%v", lipgloss.LightBlueB.Render(path), lipgloss.BlueSky.Render(" ? (y/n): "))))
fmt.Printf(lipgloss.BlueSky.Render(fmt.Sprintf("Do you want to accept the change for file '%v'%s", lipgloss.LightBlueB.Render(path), lipgloss.BlueSky.Render(" ? (y/n): "))))

for {
// Read user input
Expand Down

0 comments on commit 6a5d9e5

Please sign in to comment.