feat: add AI integration with Ollama and LangChain#2
Conversation
- integrate Viper for enhanced configuration management - add comprehensive AI configuration structure with model settings - implement 'ask' command for querying Ollama models - add LangChain Go dependency for LLM interactions - create example config.yaml with extensive AI settings
There was a problem hiding this comment.
Pull Request Overview
This PR adds AI integration capabilities to the workie project by incorporating Ollama and LangChain for local LLM interactions. The implementation provides a new CLI command for querying AI models while enhancing the configuration system with Viper.
Key changes include:
- Enhanced configuration management with Viper library for robust settings handling
- New AI configuration structure supporting comprehensive model and provider settings
- Implementation of an 'ask' command for direct CLI interaction with Ollama models
Reviewed Changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| go.mod | Updated Go version and added dependencies for LangChain, Viper, and related packages |
| config/config.go | Enhanced configuration management with new AI config structures and Viper integration |
| config.yaml | Comprehensive AI configuration template with extensive model, task, and integration settings |
| cmd/ask.go | New command implementation for querying Ollama models through CLI |
| viper.SetConfigType("yaml") | ||
| viper.SetConfigFile(configFile) | ||
|
|
||
| // Basic check if content looks like YAML (should contain colons or hyphens) | ||
| if !strings.Contains(content, ":") && !strings.Contains(content, "-") { | ||
| return nil, fmt.Errorf("config file does not appear to contain valid YAML: %s", configFile) | ||
| } | ||
| if err := viper.ReadInConfig(); err != nil { | ||
| return nil, fmt.Errorf("error reading config file: %w", err) | ||
| } | ||
|
|
||
| if err := yaml.Unmarshal(data, config); err != nil { | ||
| // Provide more helpful YAML error messages | ||
| errorStr := err.Error() | ||
| if strings.Contains(errorStr, "line") && strings.Contains(errorStr, "column") { | ||
| return nil, fmt.Errorf("failed to parse YAML config file %s: %s\n\nCommon YAML issues:\n • Check indentation (use spaces, not tabs)\n • Ensure colons are followed by spaces\n • Quote strings containing special characters\n • Verify bracket/brace matching", configFile, errorStr) | ||
| } | ||
| return nil, fmt.Errorf("failed to parse YAML config file %s: %w\n\nThis usually means the YAML syntax is invalid. Please check:\n • File uses proper YAML format\n • Indentation is consistent (use spaces)\n • All keys and values are properly quoted if needed", configFile, err) | ||
| } | ||
| if err := viper.Unmarshal(config); err != nil { |
There was a problem hiding this comment.
This line appears to be incorrectly placed in the middle of the LoadConfig function and uses a global viper instance instead of the local config variable. This could cause runtime errors and configuration conflicts.
| viper.SetConfigType("yaml") | ||
| viper.SetConfigFile(configFile) | ||
|
|
||
| // Basic check if content looks like YAML (should contain colons or hyphens) | ||
| if !strings.Contains(content, ":") && !strings.Contains(content, "-") { | ||
| return nil, fmt.Errorf("config file does not appear to contain valid YAML: %s", configFile) | ||
| } | ||
| if err := viper.ReadInConfig(); err != nil { | ||
| return nil, fmt.Errorf("error reading config file: %w", err) | ||
| } | ||
|
|
||
| if err := yaml.Unmarshal(data, config); err != nil { | ||
| // Provide more helpful YAML error messages | ||
| errorStr := err.Error() | ||
| if strings.Contains(errorStr, "line") && strings.Contains(errorStr, "column") { | ||
| return nil, fmt.Errorf("failed to parse YAML config file %s: %s\n\nCommon YAML issues:\n • Check indentation (use spaces, not tabs)\n • Ensure colons are followed by spaces\n • Quote strings containing special characters\n • Verify bracket/brace matching", configFile, errorStr) | ||
| } | ||
| return nil, fmt.Errorf("failed to parse YAML config file %s: %w\n\nThis usually means the YAML syntax is invalid. Please check:\n • File uses proper YAML format\n • Indentation is consistent (use spaces)\n • All keys and values are properly quoted if needed", configFile, err) | ||
| } | ||
| if err := viper.Unmarshal(config); err != nil { |
There was a problem hiding this comment.
This line uses the global viper instance instead of a local instance, which could cause conflicts with other parts of the application using viper.
| viper.SetConfigType("yaml") | ||
| viper.SetConfigFile(configFile) | ||
|
|
||
| // Basic check if content looks like YAML (should contain colons or hyphens) | ||
| if !strings.Contains(content, ":") && !strings.Contains(content, "-") { | ||
| return nil, fmt.Errorf("config file does not appear to contain valid YAML: %s", configFile) | ||
| } | ||
| if err := viper.ReadInConfig(); err != nil { | ||
| return nil, fmt.Errorf("error reading config file: %w", err) | ||
| } | ||
|
|
||
| if err := yaml.Unmarshal(data, config); err != nil { | ||
| // Provide more helpful YAML error messages | ||
| errorStr := err.Error() | ||
| if strings.Contains(errorStr, "line") && strings.Contains(errorStr, "column") { | ||
| return nil, fmt.Errorf("failed to parse YAML config file %s: %s\n\nCommon YAML issues:\n • Check indentation (use spaces, not tabs)\n • Ensure colons are followed by spaces\n • Quote strings containing special characters\n • Verify bracket/brace matching", configFile, errorStr) | ||
| } | ||
| return nil, fmt.Errorf("failed to parse YAML config file %s: %w\n\nThis usually means the YAML syntax is invalid. Please check:\n • File uses proper YAML format\n • Indentation is consistent (use spaces)\n • All keys and values are properly quoted if needed", configFile, err) | ||
| } | ||
| if err := viper.Unmarshal(config); err != nil { |
There was a problem hiding this comment.
This line uses the global viper instance and appears to be replacing the original YAML parsing logic incorrectly, potentially breaking the LoadConfig function.
| viper.SetConfigType("yaml") | ||
| viper.SetConfigFile(configFile) | ||
|
|
||
| // Basic check if content looks like YAML (should contain colons or hyphens) | ||
| if !strings.Contains(content, ":") && !strings.Contains(content, "-") { | ||
| return nil, fmt.Errorf("config file does not appear to contain valid YAML: %s", configFile) | ||
| } | ||
| if err := viper.ReadInConfig(); err != nil { | ||
| return nil, fmt.Errorf("error reading config file: %w", err) | ||
| } | ||
|
|
||
| if err := yaml.Unmarshal(data, config); err != nil { | ||
| // Provide more helpful YAML error messages | ||
| errorStr := err.Error() | ||
| if strings.Contains(errorStr, "line") && strings.Contains(errorStr, "column") { | ||
| return nil, fmt.Errorf("failed to parse YAML config file %s: %s\n\nCommon YAML issues:\n • Check indentation (use spaces, not tabs)\n • Ensure colons are followed by spaces\n • Quote strings containing special characters\n • Verify bracket/brace matching", configFile, errorStr) | ||
| } | ||
| return nil, fmt.Errorf("failed to parse YAML config file %s: %w\n\nThis usually means the YAML syntax is invalid. Please check:\n • File uses proper YAML format\n • Indentation is consistent (use spaces)\n • All keys and values are properly quoted if needed", configFile, err) | ||
| } | ||
| if err := viper.Unmarshal(config); err != nil { |
There was a problem hiding this comment.
This line uses the global viper instance instead of a local instance, which could cause configuration conflicts and inconsistent behavior.
| // Load configuration using Viper | ||
| config, err := config.LoadConfigWithViper("./", "config.yaml") |
There was a problem hiding this comment.
The hardcoded config path "config.yaml" reduces flexibility. Consider making this configurable through a flag or environment variable to allow users to specify different config locations.
| // Load configuration using Viper | |
| config, err := config.LoadConfigWithViper("./", "config.yaml") | |
| // Resolve configuration path | |
| configPath, _ := cmd.Flags().GetString("config") | |
| if configPath == "" { | |
| configPath = os.Getenv("CONFIG_PATH") | |
| if configPath == "" { | |
| configPath = "config.yaml" | |
| } | |
| } | |
| // Load configuration using Viper | |
| config, err := config.LoadConfigWithViper("./", configPath) |
| os.Exit(1) | ||
| } | ||
|
|
||
| fmt.Println("AI Response:", response) |
There was a problem hiding this comment.
[nitpick] The output format hardcodes "AI Response:" prefix. Consider making the output format configurable or removing the prefix for cleaner integration with scripts and pipes.
| fmt.Println("AI Response:", response) | |
| noPrefix, _ := cmd.Flags().GetBool("no-prefix") | |
| if noPrefix { | |
| fmt.Println(response) | |
| } else { | |
| fmt.Println("AI Response:", response) | |
| } |
- fixes deprecated action version error in GitHub Actions - v3 is deprecated as of April 16, 2024
- handle error returns from rootCmd.MarkFlagFilename - handle error returns from cmd.Help() - handle error returns from cmd.Process.Kill() - fix nil pointer dereferences in config tests by using t.Fatal - fix undefined variable reference in manager.go
- remove invalid go version format (1.22.0 -> 1.21) - remove unsupported toolchain directive - fixes go mod download error in CI
- fix gosec action from securecodewarrior to securego - update Go versions to match go.mod (remove 1.22, use 1.21) - update actions/cache from v3 to v4
- change build path from ./cmd/workie to . (root directory) - main.go is in the root directory, not in cmd/workie
Overview
This PR adds AI integration capabilities to the workie project by integrating Ollama and LangChain for local LLM interactions.
Changes
Files Changed
cmd/ask.go- New command for AI interactionsconfig.yaml- Comprehensive configuration templateconfig/config.go- Enhanced configuration management with Vipergo.mod&go.sum- Added dependencies for Ollama and LangChainFeatures
Usage
After installing Ollama locally, users can:
workie ask "your question here"commandThis enhancement makes workie more powerful by adding AI-assisted functionality while maintaining privacy through local model execution.