Skip to content

Commit dbb0d49

Browse files
authored
Merge pull request #18 from ai-coding-workshop/jx/config-option
feat: add config file support
2 parents 0278a0c + e7bc6e2 commit dbb0d49

3 files changed

Lines changed: 85 additions & 7 deletions

File tree

‎.dict-be.yml.example‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
llm:
2+
url: "https://api.openai.com/v1"
3+
model: "gpt-4o-mini"
4+
token: "your-api-token"
5+
type: "openai"

‎internal/cli/root.go‎

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ package cli
33
import (
44
"fmt"
55
"os"
6+
"path/filepath"
7+
"strings"
8+
9+
"dict-be/internal/config"
610

711
"github.com/spf13/cobra"
812
"github.com/spf13/viper"
@@ -14,6 +18,10 @@ type Options struct {
1418

1519
func NewRootCmd() *cobra.Command {
1620
opts := &Options{}
21+
defaultConfigPath := ""
22+
if homeDir, err := os.UserHomeDir(); err == nil {
23+
defaultConfigPath = filepath.Join(homeDir, ".dict-be.yml")
24+
}
1725
root := &cobra.Command{
1826
Use: "dict-be",
1927
Short: "dict-be - CLI starter",
@@ -26,8 +34,8 @@ func NewRootCmd() *cobra.Command {
2634
root.PersistentFlags().StringVar(
2735
&opts.Config,
2836
"config",
29-
"",
30-
"config file (default: ./dict-be.yaml)",
37+
defaultConfigPath,
38+
"config file (default: ~/.dict-be.yml)",
3139
)
3240
_ = viper.BindPFlag("config", root.PersistentFlags().Lookup("config"))
3341

@@ -36,22 +44,46 @@ func NewRootCmd() *cobra.Command {
3644
}
3745

3846
func initConfig(configFile string) {
47+
configFile = strings.TrimSpace(configFile)
3948
if configFile != "" {
40-
viper.SetConfigFile(configFile)
49+
viper.SetConfigFile(expandHome(configFile))
4150
} else {
42-
viper.SetConfigName("dict-be")
43-
viper.SetConfigType("yaml")
44-
viper.AddConfigPath(".")
45-
viper.AddConfigPath("$HOME/.config/dict-be")
51+
if homeDir, err := os.UserHomeDir(); err == nil {
52+
viper.SetConfigFile(filepath.Join(homeDir, ".dict-be.yml"))
53+
}
4654
}
4755

4856
viper.SetEnvPrefix("DICT_BE")
4957
viper.AutomaticEnv()
58+
viper.SetDefault("llm.url", "")
59+
viper.SetDefault("llm.model", "")
60+
viper.SetDefault("llm.token", "")
61+
viper.SetDefault("llm.type", "")
5062

5163
if err := viper.ReadInConfig(); err != nil {
5264
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
5365
return
5466
}
5567
fmt.Fprintln(os.Stderr, err.Error())
68+
return
69+
}
70+
71+
if _, err := config.Load(); err != nil {
72+
fmt.Fprintln(os.Stderr, err.Error())
73+
}
74+
}
75+
76+
func expandHome(path string) string {
77+
if path == "~" {
78+
if homeDir, err := os.UserHomeDir(); err == nil {
79+
return homeDir
80+
}
81+
return path
82+
}
83+
if strings.HasPrefix(path, "~/") {
84+
if homeDir, err := os.UserHomeDir(); err == nil {
85+
return filepath.Join(homeDir, strings.TrimPrefix(path, "~/"))
86+
}
5687
}
88+
return path
5789
}

‎internal/config/config.go‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package config
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/spf13/viper"
7+
)
8+
9+
type Config struct {
10+
LLM LLMConfig `mapstructure:"llm"`
11+
}
12+
13+
type LLMConfig struct {
14+
URL string `mapstructure:"url"`
15+
Model string `mapstructure:"model"`
16+
Token string `mapstructure:"token"`
17+
Type string `mapstructure:"type"`
18+
}
19+
20+
func Load() (Config, error) {
21+
var cfg Config
22+
if err := viper.Unmarshal(&cfg); err != nil {
23+
return cfg, err
24+
}
25+
if err := cfg.Validate(); err != nil {
26+
return cfg, err
27+
}
28+
return cfg, nil
29+
}
30+
31+
func (c Config) Validate() error {
32+
if c.LLM.Type == "" {
33+
return nil
34+
}
35+
switch c.LLM.Type {
36+
case "openai", "anthropics", "gemini":
37+
return nil
38+
default:
39+
return fmt.Errorf("invalid llm.type: %s", c.LLM.Type)
40+
}
41+
}

0 commit comments

Comments
 (0)