1
1
import os
2
2
import json
3
- from typing import Optional
3
+ from typing import Optional , Union
4
4
from dataclasses import dataclass
5
5
from pathlib import Path
6
6
from dotenv import load_dotenv
@@ -32,6 +32,45 @@ class VectorDbConfig:
32
32
host : str
33
33
34
34
35
+ def _get_config_value (key : str , config_raw : dict , default : Optional [str ] = None ) -> str :
36
+ """Get configuration value with priority: ENV > .env > JSON."""
37
+ # First check environment variables
38
+ env_value = os .getenv (key )
39
+ if env_value is not None :
40
+ return env_value
41
+
42
+ # Then check nested JSON structure
43
+ keys = key .lower ().split ('_' )
44
+ # Skip the 'gc_qa_rag' prefix for JSON lookup
45
+ if len (keys ) >= 4 and keys [0 ] == 'gc' and keys [1 ] == 'qa' and keys [2 ] == 'rag' :
46
+ keys = keys [3 :]
47
+
48
+ current = config_raw
49
+ try :
50
+ for k in keys :
51
+ current = current [k ]
52
+ return str (current )
53
+ except (KeyError , TypeError ):
54
+ pass
55
+
56
+ # Return default if provided
57
+ if default is not None :
58
+ return default
59
+
60
+ raise ValueError (f"Configuration value not found for key: { key } " )
61
+
62
+
63
+ def _get_config_int (key : str , config_raw : dict , default : Optional [int ] = None ) -> int :
64
+ """Get integer configuration value with priority: ENV > .env > JSON."""
65
+ value = _get_config_value (key , config_raw , str (default ) if default is not None else None )
66
+ try :
67
+ return int (value )
68
+ except (ValueError , TypeError ):
69
+ if default is not None :
70
+ return default
71
+ raise ValueError (f"Invalid integer value for key { key } : { value } " )
72
+
73
+
35
74
@dataclass
36
75
class Config :
37
76
environment : str
@@ -45,47 +84,46 @@ class Config:
45
84
@classmethod
46
85
def from_environment (cls , environment : str ) -> "Config" :
47
86
"""Create a Config instance from environment name."""
87
+ # Load .env file first (lower priority than direct env vars)
88
+ load_dotenv ()
89
+
90
+ # Try to load JSON config, but make it optional
91
+ config_raw = {}
48
92
config_path = Path (f".config.{ environment } .json" )
49
- if not config_path .exists ():
50
- raise FileNotFoundError (f"Configuration file not found: { config_path } " )
51
-
52
- try :
53
- with open (config_path ) as f :
54
- config_raw = json .load (f )
55
- except json .JSONDecodeError as e :
56
- raise ValueError (f"Invalid JSON in configuration file: { e } " )
93
+ if config_path .exists ():
94
+ try :
95
+ with open (config_path ) as f :
96
+ config_raw = json .load (f )
97
+ except json .JSONDecodeError as e :
98
+ print (f"Warning: Invalid JSON in configuration file: { e } " )
57
99
58
100
return cls (
59
101
environment = environment ,
60
102
das = DasConfig (
61
- base_url_page = config_raw [ "das" ][ "base_url_page" ] ,
62
- base_url_thread = config_raw [ "das" ][ "base_url_thread" ] ,
63
- token = config_raw [ "das" ][ "token" ] ,
103
+ base_url_page = _get_config_value ( "GC_QA_RAG_DAS_BASE_URL_PAGE" , config_raw , "" ) ,
104
+ base_url_thread = _get_config_value ( "GC_QA_RAG_DAS_BASE_URL_THREAD" , config_raw , "" ) ,
105
+ token = _get_config_value ( "GC_QA_RAG_DAS_TOKEN" , config_raw , "" ) ,
64
106
),
65
107
llm = LlmConfig (
66
- api_key = config_raw [ "llm" ][ "api_key" ] ,
67
- api_base = config_raw [ "llm" ][ "api_base" ] ,
68
- model_name = config_raw [ "llm" ][ "model_name" ] ,
69
- max_rpm = config_raw [ "llm" ]. get ( "max_rpm" , 100 ),
108
+ api_key = _get_config_value ( "GC_QA_RAG_LLM_API_KEY" , config_raw ) ,
109
+ api_base = _get_config_value ( "GC_QA_RAG_LLM_API_BASE" , config_raw , "https://dashscope.aliyuncs.com/compatible-mode/v1" ) ,
110
+ model_name = _get_config_value ( "GC_QA_RAG_LLM_MODEL_NAME" , config_raw , "qwen-plus" ) ,
111
+ max_rpm = _get_config_int ( "GC_QA_RAG_LLM_MAX_RPM" , config_raw , 100 ),
70
112
),
71
- embedding = EmbeddingConfig (api_key = config_raw ["embedding" ]["api_key" ]),
72
- vector_db = VectorDbConfig (host = config_raw ["vector_db" ]["host" ]),
73
- root_path = config_raw .get (
74
- "root_path" , user_cache_dir ("gc-qa-rag" , ensure_exists = True )
113
+ embedding = EmbeddingConfig (
114
+ api_key = _get_config_value ("GC_QA_RAG_EMBEDDING_API_KEY" , config_raw )
75
115
),
76
- log_path = config_raw . get (
77
- "log_path " , user_log_dir ( "gc-qa-rag" , ensure_exists = True )
116
+ vector_db = VectorDbConfig (
117
+ host = _get_config_value ( "GC_QA_RAG_VECTOR_DB_HOST " , config_raw , "http://host.docker.internal:6333" )
78
118
),
119
+ root_path = _get_config_value ("GC_QA_RAG_ROOT_PATH" , config_raw , user_cache_dir ("gc-qa-rag" , ensure_exists = True )),
120
+ log_path = _get_config_value ("GC_QA_RAG_LOG_PATH" , config_raw , user_log_dir ("gc-qa-rag" , ensure_exists = True )),
79
121
)
80
122
81
123
82
124
def get_config () -> Config :
83
125
"""Get the application configuration."""
84
- load_dotenv ()
85
- environment = os .getenv ("GC_QA_RAG_ENV" )
86
- if not environment :
87
- raise ValueError ("GC_QA_RAG_ENV environment variable is not set" )
88
-
126
+ environment = os .getenv ("GC_QA_RAG_ENV" , "production" )
89
127
return Config .from_environment (environment )
90
128
91
129
0 commit comments