Skip to content

Commit 3da980e

Browse files
authored
Merge pull request #170 from #158
158 기능 selectbox 엔진 옵션 확장
2 parents be95434 + 32e3174 commit 3da980e

File tree

5 files changed

+240
-6
lines changed

5 files changed

+240
-6
lines changed

engine/query_executor.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,26 @@ def execute_query(
8282
"retriever_name": retriever_name,
8383
"top_n": top_n,
8484
"device": device,
85+
# 다이얼렉트 정보 주입 (있다면 세션에서, 없으면 기본값)
86+
"dialect_name": (
87+
session_state.get("selected_dialect_option", {}).get("name")
88+
if session_state is not None
89+
else database_env
90+
),
91+
"supports_ilike": (
92+
bool(
93+
session_state.get("selected_dialect_option", {}).get(
94+
"supports_ilike", False
95+
)
96+
)
97+
if session_state is not None
98+
else False
99+
),
100+
"dialect_hints": (
101+
session_state.get("selected_dialect_option", {}).get("hints", [])
102+
if session_state is not None
103+
else []
104+
),
85105
}
86106
)
87107

interface/dialects.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
"""
2+
다이얼렉트 프리셋과 옵션 정의 모듈.
3+
4+
이 모듈은 다음을 제공합니다:
5+
6+
- DialectOption: 각 SQL 엔진 특성 데이터클래스
7+
- name: 엔진 표시 이름 (예: "PostgreSQL", "ClickHouse")
8+
- supports_ilike: 대소문자 무시 비교(ILIKE) 지원 여부
9+
- hints: 자주 쓰이는/효과적인 함수의 간결 목록 + 짧은 메모
10+
- 예: ["DATE_TRUNC (날짜 절단)", "STRING_AGG (문자 집계)"]
11+
12+
- PRESET_DIALECTS: 대표 SQL 엔진들의 기본 프리셋 모음
13+
- PostgreSQL, ClickHouse, Trino, Snowflake, Redshift, BigQuery, MSSQL, Oracle, DuckDB
14+
15+
주 사용처:
16+
- Streamlit UI에서 프리셋 선택 및 커스텀 다이얼렉트 입력의 기준 데이터
17+
- Lang2SQL 파이프라인에서 프롬프트/키워드 힌트 구성
18+
19+
주의:
20+
- hints는 프롬프트 가이드용이며 실행 보장을 의미하지 않습니다.
21+
- 실제 문법/함수 지원은 엔진 버전 및 설정에 따라 달라질 수 있습니다.
22+
"""
23+
24+
from __future__ import annotations
25+
26+
from dataclasses import asdict, dataclass, field
27+
from typing import Dict, List
28+
29+
30+
@dataclass
31+
class DialectOption:
32+
name: str
33+
supports_ilike: bool = False
34+
hints: List[str] = field(default_factory=list)
35+
36+
def to_dict(self) -> Dict:
37+
return asdict(self)
38+
39+
@staticmethod
40+
def from_dict(data: Dict) -> "DialectOption":
41+
return DialectOption(
42+
name=data.get("name", "Custom"),
43+
supports_ilike=bool(data.get("supports_ilike", False)),
44+
hints=list(data.get("hints", data.get("keyword_hints", []))),
45+
)
46+
47+
48+
PRESET_DIALECTS: Dict[str, DialectOption] = {
49+
"PostgreSQL": DialectOption(
50+
name="PostgreSQL",
51+
supports_ilike=True,
52+
hints=[
53+
"COALESCE (널 대체)",
54+
"DATE_TRUNC (날짜 절단)",
55+
"STRING_AGG (문자 집계)",
56+
"GENERATE_SERIES (시퀀스 생성)",
57+
],
58+
),
59+
"ClickHouse": DialectOption(
60+
name="ClickHouse",
61+
supports_ilike=False,
62+
hints=[
63+
"toDate (날짜 변환)",
64+
"dateDiff (날짜 차이)",
65+
"arrayJoin (배열 펼치기)",
66+
"groupArray (배열 집계)",
67+
],
68+
),
69+
"Trino": DialectOption(
70+
name="Trino",
71+
supports_ilike=False,
72+
hints=[
73+
"date_trunc (날짜 절단)",
74+
"try_cast (안전 변환)",
75+
"coalesce (널 대체)",
76+
"regexp_like (정규식 매칭)",
77+
],
78+
),
79+
"Snowflake": DialectOption(
80+
name="Snowflake",
81+
supports_ilike=True,
82+
hints=[
83+
"IFF (조건 분기)",
84+
"TO_DATE (날짜 변환)",
85+
"DATE_TRUNC (날짜 절단)",
86+
"LISTAGG (문자 집계)",
87+
],
88+
),
89+
"Redshift": DialectOption(
90+
name="Redshift",
91+
supports_ilike=True,
92+
hints=[
93+
"COALESCE (널 대체)",
94+
"DATE_TRUNC (날짜 절단)",
95+
"LISTAGG (문자 집계)",
96+
"REGEXP_REPLACE (정규식 치환)",
97+
],
98+
),
99+
"BigQuery": DialectOption(
100+
name="BigQuery",
101+
supports_ilike=False,
102+
hints=[
103+
"SAFE_CAST (안전 변환)",
104+
"DATE_TRUNC (날짜 절단)",
105+
"ARRAY_AGG (배열 집계)",
106+
"REGEXP_CONTAINS (정규식 포함)",
107+
],
108+
),
109+
"MSSQL": DialectOption(
110+
name="MSSQL",
111+
supports_ilike=False,
112+
hints=[
113+
"ISNULL (널 대체)",
114+
"DATEADD (날짜 가감)",
115+
"CONVERT (형 변환)",
116+
"STRING_AGG (문자 집계)",
117+
],
118+
),
119+
"Oracle": DialectOption(
120+
name="Oracle",
121+
supports_ilike=False,
122+
hints=[
123+
"NVL (널 대체)",
124+
"TO_DATE (날짜 변환)",
125+
"TRUNC (날짜 절단)",
126+
"LISTAGG (문자 집계)",
127+
],
128+
),
129+
"DuckDB": DialectOption(
130+
name="DuckDB",
131+
supports_ilike=True,
132+
hints=[
133+
"date_trunc (날짜 절단)",
134+
"string_agg (문자 집계)",
135+
"coalesce (널 대체)",
136+
"regexp_replace (정규식 치환)",
137+
],
138+
),
139+
}

interface/lang2sql.py

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88
import re
99

1010
import streamlit as st
11-
from langchain.chains.sql_database.prompt import SQL_PROMPTS
1211
from langchain_core.messages import AIMessage
12+
from interface.dialects import PRESET_DIALECTS, DialectOption
13+
from copy import deepcopy
1314

1415
from db_utils import get_db_connector
1516
from db_utils.base_connector import BaseConnector
@@ -339,11 +340,70 @@ def _as_float(value):
339340
"쿼리를 입력하세요:",
340341
value=DEFAULT_QUERY,
341342
)
342-
user_database_env = st.selectbox(
343-
"DB 환경정보를 입력하세요:",
344-
options=SQL_PROMPTS.keys(),
345-
index=0,
346-
)
343+
344+
# DB 프리셋을 세션에 로드(편집 가능)
345+
if "dialects" not in st.session_state:
346+
st.session_state["dialects"] = {k: v.to_dict() for k, v in PRESET_DIALECTS.items()}
347+
348+
st.markdown("### DB 선택 및 관리")
349+
cols = st.columns(2)
350+
351+
# 공통 변수 최소화
352+
dialects = st.session_state["dialects"]
353+
keys = list(dialects.keys())
354+
active = st.session_state.get("active_dialect", keys[0])
355+
356+
with cols[0]:
357+
user_database_env = st.selectbox(
358+
"사용할 DB를 선택하세요:",
359+
options=keys,
360+
index=(keys.index(active) if active in keys else 0),
361+
)
362+
st.session_state["active_dialect"] = user_database_env
363+
st.session_state["selected_dialect_option"] = dialects.get(
364+
user_database_env, dialects[keys[0]]
365+
)
366+
367+
with cols[1]:
368+
st.caption("선택된 DB 설정을 편집하거나 새로 추가할 수 있습니다.")
369+
370+
with st.expander("DB 편집"):
371+
edit_key = st.selectbox(
372+
"편집할 DB를 선택하세요:",
373+
options=keys,
374+
index=(
375+
keys.index(st.session_state["active_dialect"])
376+
if st.session_state.get("active_dialect") in keys
377+
else 0
378+
),
379+
key="dialect_edit_selector",
380+
)
381+
# 편집 대상 선택 시 메인 선택과 동기화
382+
st.session_state["active_dialect"] = edit_key
383+
st.session_state["selected_dialect_option"] = dialects[edit_key]
384+
385+
current = deepcopy(dialects[edit_key])
386+
_supports_ilike = st.checkbox(
387+
"ILIKE 지원", value=bool(current.get("supports_ilike", False))
388+
)
389+
# limit_syntax 제거: hints로 사용자가 커버
390+
_hints_text = st.text_area(
391+
"hints (쉼표로 구분)",
392+
value=", ".join(current.get("hints", [])),
393+
help="예약어/함수/문법 힌트를 쉼표로 구분하여 입력",
394+
)
395+
if st.button("변경사항 저장", key="btn_save_dialect_edit"):
396+
st.session_state["dialects"][edit_key] = DialectOption(
397+
name=edit_key,
398+
supports_ilike=_supports_ilike,
399+
hints=[s.strip() for s in _hints_text.split(",") if s.strip()],
400+
).to_dict()
401+
# 저장 후 선택된 다이얼렉트 옵션도 최신 값으로 동기화
402+
st.session_state["selected_dialect_option"] = st.session_state["dialects"][
403+
edit_key
404+
]
405+
st.success(f"{edit_key} DB가 업데이트되었습니다.")
406+
347407

348408
_device_options = ["cpu", "cuda"]
349409
_default_device = st.session_state.get("default_device", "cpu")

llm_utils/graph_utils/base.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ class QueryMakerState(TypedDict):
3838
top_n: int
3939
device: str
4040
question_gate_result: dict
41+
# 다이얼렉트 정보
42+
dialect_name: str
43+
supports_ilike: bool
44+
dialect_hints: list[str]
4145

4246

4347
# 노드 함수: QUESTION_GATE 노드
@@ -245,6 +249,10 @@ def query_maker_node(state: QueryMakerState):
245249
"user_input": combined_input,
246250
"user_database_env": state["user_database_env"],
247251
"searched_tables": searched_tables_json,
252+
# 다이얼렉트 변수 전달
253+
"dialect_name": state.get("dialect_name", ""),
254+
"supports_ilike": state.get("supports_ilike", False),
255+
"dialect_hints": ", ".join(state.get("dialect_hints", [])),
248256
}
249257
)
250258
state["generated_query"] = res

prompt/query_maker_prompt.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
# 주의사항
77
- 사용자의 질문이 다소 모호하더라도, 주어진 데이터를 참고하여 합리적인 가정을 통해 SQL 쿼리를 완성하세요.
88
- 불필요한 재질문 없이, 가능한 가장 명확한 분석 쿼리를 만들어 주세요.
9+
- 반드시 입력된 다이얼렉트 변수들을 준수하여 문법을 선택하세요.
910
- 최종 출력 형식은 반드시 아래와 같아야 합니다.
1011

1112
# Output Example
@@ -34,7 +35,13 @@
3435
- 관련 테이블 및 컬럼 정보:
3536
{searched_tables}
3637

38+
- 다이얼렉트 정보:
39+
- dialect_name: {dialect_name}
40+
- supports_ilike: {supports_ilike}
41+
- dialect_hints: {dialect_hints}
42+
3743
# Notes
3844

3945
- 위 입력을 바탕으로 최적의 SQL을 생성하세요.
46+
- {dialect_hints}를 참고하여 엔진에 맞는 함수/연산자를 우선 사용하세요.
4047
- 출력은 위 '최종 형태 예시'와 동일한 구조로만 작성하세요.

0 commit comments

Comments
 (0)