11import os
22import json
3- from typing import Optional
3+ from typing import Optional , Union
44from dataclasses import dataclass
55from pathlib import Path
66from dotenv import load_dotenv
@@ -32,6 +32,45 @@ class VectorDbConfig:
3232 host : str
3333
3434
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+
3574@dataclass
3675class Config :
3776 environment : str
@@ -45,47 +84,46 @@ class Config:
4584 @classmethod
4685 def from_environment (cls , environment : str ) -> "Config" :
4786 """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 = {}
4892 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 } " )
5799
58100 return cls (
59101 environment = environment ,
60102 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 , "" ) ,
64106 ),
65107 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 ),
70112 ),
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 )
75115 ),
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" )
78118 ),
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 )),
79121 )
80122
81123
82124def get_config () -> Config :
83125 """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" )
89127 return Config .from_environment (environment )
90128
91129
0 commit comments