Skip to content

Commit 1275902

Browse files
Merge pull request #168 from CausalInferenceLab/167-refactor-cli-split-modules
refactor(cli): CLI 코드를 모듈 단위로 분리하여 구조 개선
2 parents 7371ea6 + 57b0929 commit 1275902

File tree

7 files changed

+351
-249
lines changed

7 files changed

+351
-249
lines changed

cli/__init__.py

Lines changed: 22 additions & 249 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,24 @@
1-
"""
2-
Lang2SQL CLI 프로그램입니다.
1+
"""Lang2SQL CLI 프로그램입니다.
32
이 프로그램은 Datahub GMS 서버 URL을 설정하고, 필요 시 Streamlit 인터페이스를 실행합니다.
43
54
명령어 예시: lang2sql --datahub_server http://localhost:8080 --run-streamlit
65
"""
76

8-
import logging
9-
import os
10-
import subprocess
11-
127
import click
13-
import dotenv
148

9+
from cli.commands.quary import query_command
10+
from cli.commands.run_streamlit import run_streamlit_cli_command
11+
from cli.core.environment import initialize_environment
12+
from cli.core.streamlit_runner import run_streamlit_command
13+
from cli.utils.logger import configure_logging
1514
from infra.monitoring.check_server import CheckServer
1615
from llm_utils.tools import set_gms_server
1716
from version import __version__
1817

19-
logging.basicConfig(
20-
level=logging.INFO,
21-
format="%(asctime)s [%(levelname)s] %(message)s",
22-
datefmt="%Y-%m-%d %H:%M:%S",
23-
)
24-
logger = logging.getLogger(__name__)
18+
logger = configure_logging()
2519

2620

21+
# pylint: disable=redefined-outer-name,broad-exception-caught
2722
@click.group()
2823
@click.version_option(version=__version__)
2924
@click.pass_context
@@ -79,7 +74,6 @@
7974
"기본값: FAISS는 './dev/table_info_db', pgvector는 환경변수 사용"
8075
),
8176
)
82-
# pylint: disable=redefined-outer-name
8377
def cli(
8478
ctx: click.Context,
8579
datahub_server: str,
@@ -90,69 +84,24 @@ def cli(
9084
vectordb_type: str = "faiss",
9185
vectordb_location: str = None,
9286
) -> None:
93-
"""
94-
Datahub GMS 서버 URL을 설정하고, Streamlit 애플리케이션을 실행할 수 있는 CLI 명령 그룹입니다.
95-
96-
이 함수는 다음 역할을 수행합니다:
97-
- 전달받은 'datahub_server' URL을 바탕으로 GMS 서버 연결을 설정합니다.
98-
- 설정 과정 중 오류가 발생하면 오류 메시지를 출력하고 프로그램을 종료합니다.
99-
- '--run-streamlit' 옵션이 활성화된 경우, 지정된 포트에서 Streamlit 웹 앱을 즉시 실행합니다.
100-
- '--env-file-path' 옵션이 지정된 경우, 해당 .env 파일에서 환경 변수를 로드합니다.
101-
- '--prompt-dir-path' 옵션이 지정된 경우, 해당 디렉토리에서 프롬프트 템플릿을 로드합니다.
102-
103-
매개변수:
104-
ctx (click.Context): 명령어 실행 컨텍스트 객체입니다.
105-
datahub_server (str): 설정할 Datahub GMS 서버의 URL입니다.
106-
run_streamlit (bool): Streamlit 앱을 실행할지 여부를 나타내는 플래그입니다.
107-
port (int): Streamlit 서버가 바인딩될 포트 번호입니다.
108-
env_file_path (str, optional): 환경 변수를 로드할 .env 파일 경로입니다.
109-
prompt_dir_path (str, optional): 프롬프트 템플릿을 로드할 디렉토리 경로입니다.
87+
"""Lang2SQL CLI 엔트리포인트.
11088
111-
주의:
112-
'set_gms_server' 함수에서 ValueError가 발생할 경우, 프로그램은 비정상 종료(exit code 1)합니다.
89+
- 환경 변수 및 VectorDB 설정 초기화
90+
- GMS 서버 연결 및 헬스체크
91+
- 필요 시 Streamlit 애플리케이션 실행
11392
"""
11493

115-
# 환경 변수 파일 로드
116-
if env_file_path:
117-
try:
118-
if not dotenv.load_dotenv(env_file_path, override=True):
119-
click.secho(f"환경 변수 파일 로드 실패: {env_file_path}", fg="yellow")
120-
else:
121-
click.secho(f"환경 변수 파일 로드 성공: {env_file_path}", fg="green")
122-
except Exception as e:
123-
click.secho(f"환경 변수 로드 중 오류 발생: {str(e)}", fg="red")
124-
ctx.exit(1)
125-
else:
126-
dotenv.load_dotenv(override=True)
127-
128-
# 프롬프트 디렉토리를 환경 변수로 설정
129-
if prompt_dir_path:
130-
try:
131-
os.environ["PROMPT_TEMPLATES_DIR"] = prompt_dir_path
132-
click.secho(
133-
f"프롬프트 디렉토리 환경변수 설정됨: {prompt_dir_path}", fg="green"
134-
)
135-
except Exception as e:
136-
click.secho(f"프롬프트 디렉토리 환경변수 설정 실패: {str(e)}", fg="red")
137-
ctx.exit(1)
138-
139-
# VectorDB 타입을 환경 변수로 설정
14094
try:
141-
os.environ["VECTORDB_TYPE"] = vectordb_type
142-
click.secho(f"VectorDB 타입 설정됨: {vectordb_type}", fg="green")
143-
except Exception as e:
144-
click.secho(f"VectorDB 타입 설정 실패: {str(e)}", fg="red")
95+
initialize_environment(
96+
env_file_path=env_file_path,
97+
prompt_dir_path=prompt_dir_path,
98+
vectordb_type=vectordb_type,
99+
vectordb_location=vectordb_location,
100+
)
101+
except Exception:
102+
logger.error("Initialization failed.", exc_info=True)
145103
ctx.exit(1)
146104

147-
# VectorDB 경로를 환경 변수로 설정
148-
if vectordb_location:
149-
try:
150-
os.environ["VECTORDB_LOCATION"] = vectordb_location
151-
click.secho(f"VectorDB 경로 설정됨: {vectordb_location}", fg="green")
152-
except Exception as e:
153-
click.secho(f"VectorDB 경로 설정 실패: {str(e)}", fg="red")
154-
ctx.exit(1)
155-
156105
logger.info(
157106
"Initialization started: GMS server = %s, run_streamlit = %s, port = %d",
158107
datahub_server,
@@ -171,181 +120,5 @@ def cli(
171120
run_streamlit_command(port)
172121

173122

174-
def run_streamlit_command(port: int) -> None:
175-
"""
176-
지정된 포트에서 Streamlit 애플리케이션을 실행하는 함수입니다.
177-
178-
이 함수는 subprocess를 통해 'streamlit run' 명령어를 실행하여
179-
'interface/streamlit_app.py' 파일을 웹 서버 형태로 구동합니다.
180-
사용자가 지정한 포트 번호를 Streamlit 서버의 포트로 설정합니다.
181-
182-
매개변수:
183-
port (int): Streamlit 서버가 바인딩될 포트 번호입니다.
184-
185-
주의:
186-
- Streamlit이 시스템에 설치되어 있어야 정상 동작합니다.
187-
- subprocess 호출 실패 시 예외가 발생할 수 있습니다.
188-
"""
189-
190-
logger.info("Starting Streamlit application on port %d...", port)
191-
192-
try:
193-
subprocess.run(
194-
[
195-
"streamlit",
196-
"run",
197-
"interface/streamlit_app.py",
198-
"--server.address=0.0.0.0",
199-
"--server.port",
200-
str(port),
201-
],
202-
check=True,
203-
)
204-
logger.info("Streamlit application started successfully.")
205-
except subprocess.CalledProcessError as e:
206-
logger.error("Failed to start Streamlit application: %s", e)
207-
raise
208-
209-
210-
@cli.command(name="run-streamlit")
211-
@click.option(
212-
"-p",
213-
"--port",
214-
type=int,
215-
default=8501,
216-
help=(
217-
"Streamlit 애플리케이션이 바인딩될 포트 번호를 지정합니다. "
218-
"기본 포트는 8501이며, 필요 시 포트 충돌을 피하거나 "
219-
"여러 인스턴스를 동시에 실행할 때 다른 포트 번호를 설정할 수 있습니다."
220-
),
221-
)
222-
def run_streamlit_cli_command(port: int) -> None:
223-
"""
224-
CLI 명령어를 통해 Streamlit 애플리케이션을 실행하는 함수입니다.
225-
226-
이 명령은 'interface/streamlit_app.py' 파일을 Streamlit 서버로 구동하며,
227-
사용자가 지정한 포트 번호를 바인딩하여 웹 인터페이스를 제공합니다.
228-
229-
매개변수:
230-
port (int): Streamlit 서버가 사용할 포트 번호입니다. 기본값은 8501입니다.
231-
232-
주의:
233-
- Streamlit이 시스템에 설치되어 있어야 정상적으로 실행됩니다.
234-
- Streamlit 실행에 실패할 경우 subprocess 호출에서 예외가 발생할 수 있습니다.
235-
"""
236-
237-
logger.info("Executing 'run-streamlit' command on port %d...", port)
238-
run_streamlit_command(port)
239-
240-
241-
@cli.command(name="query")
242-
@click.argument("question", type=str)
243-
@click.option(
244-
"--database-env",
245-
default="clickhouse",
246-
help="사용할 데이터베이스 환경 (기본값: clickhouse)",
247-
)
248-
@click.option(
249-
"--retriever-name",
250-
default="기본",
251-
help="테이블 검색기 이름 (기본값: 기본)",
252-
)
253-
@click.option(
254-
"--top-n",
255-
type=int,
256-
default=5,
257-
help="검색된 상위 테이블 수 제한 (기본값: 5)",
258-
)
259-
@click.option(
260-
"--device",
261-
default="cpu",
262-
help="LLM 실행에 사용할 디바이스 (기본값: cpu)",
263-
)
264-
@click.option(
265-
"--use-enriched-graph",
266-
is_flag=True,
267-
help="확장된 그래프(프로파일 추출 + 컨텍스트 보강) 사용 여부",
268-
)
269-
@click.option(
270-
"--vectordb-type",
271-
type=click.Choice(["faiss", "pgvector"]),
272-
default="faiss",
273-
help="사용할 벡터 데이터베이스 타입 (기본값: faiss)",
274-
)
275-
@click.option(
276-
"--vectordb-location",
277-
help=(
278-
"VectorDB 위치 설정\n"
279-
"- FAISS: 디렉토리 경로 (예: ./my_vectordb)\n"
280-
"- pgvector: 연결 문자열 (예: postgresql://user:pass@host:port/db)\n"
281-
"기본값: FAISS는 './dev/table_info_db', pgvector는 환경변수 사용"
282-
),
283-
)
284-
def query_command(
285-
question: str,
286-
database_env: str,
287-
retriever_name: str,
288-
top_n: int,
289-
device: str,
290-
use_enriched_graph: bool,
291-
vectordb_type: str = "faiss",
292-
vectordb_location: str = None,
293-
) -> None:
294-
"""
295-
자연어 질문을 SQL 쿼리로 변환하여 출력하는 명령어입니다.
296-
297-
이 명령은 사용자가 입력한 자연어 질문을 받아서 SQL 쿼리로 변환하고,
298-
생성된 SQL 쿼리만을 표준 출력으로 출력합니다.
299-
300-
매개변수:
301-
question (str): SQL로 변환할 자연어 질문
302-
database_env (str): 사용할 데이터베이스 환경
303-
retriever_name (str): 테이블 검색기 이름
304-
top_n (int): 검색된 상위 테이블 수 제한
305-
device (str): LLM 실행에 사용할 디바이스
306-
use_enriched_graph (bool): 확장된 그래프 사용 여부
307-
308-
예시:
309-
lang2sql query "고객 데이터를 기반으로 유니크한 유저 수를 카운트하는 쿼리"
310-
lang2sql query "고객 데이터를 기반으로 유니크한 유저 수를 카운트하는 쿼리" --use-enriched-graph
311-
lang2sql query "고객 데이터를 기반으로 유니크한 유저 수를 카운트하는 쿼리" --vectordb-type pgvector
312-
"""
313-
314-
try:
315-
from engine.query_executor import execute_query, extract_sql_from_result
316-
317-
# VectorDB 타입을 환경 변수로 설정
318-
os.environ["VECTORDB_TYPE"] = vectordb_type
319-
320-
# VectorDB 위치를 환경 변수로 설정
321-
if vectordb_location:
322-
os.environ["VECTORDB_LOCATION"] = vectordb_location
323-
324-
# 공용 함수를 사용하여 쿼리 실행
325-
res = execute_query(
326-
query=question,
327-
database_env=database_env,
328-
retriever_name=retriever_name,
329-
top_n=top_n,
330-
device=device,
331-
use_enriched_graph=use_enriched_graph,
332-
)
333-
334-
# SQL 추출 및 출력
335-
sql = extract_sql_from_result(res)
336-
if sql:
337-
print(sql)
338-
else:
339-
# SQL 추출 실패 시 원본 쿼리 텍스트 출력
340-
generated_query = res.get("generated_query")
341-
if generated_query:
342-
query_text = (
343-
generated_query.content
344-
if hasattr(generated_query, "content")
345-
else str(generated_query)
346-
)
347-
print(query_text)
348-
349-
except Exception as e:
350-
logger.error("쿼리 처리 중 오류 발생: %s", e)
351-
raise
123+
cli.add_command(run_streamlit_cli_command)
124+
cli.add_command(query_command)

0 commit comments

Comments
 (0)