Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
47e880a
chore : internal spec added
seyoung4503 Feb 22, 2026
53ba13e
Merge remote-tracking branch 'origin/master' into feat/e2e-pipeline
seyoung4503 Feb 22, 2026
790804c
chore: remove local-only ignore rule
seyoung4503 Feb 22, 2026
6671b50
feat(core): add CatalogEntry TypedDict as explicit schema contract
seyoung4503 Feb 23, 2026
7ae2062
refactor(core): remove RunContext contract check and run_query() from…
seyoung4503 Feb 23, 2026
1210917
refactor(flows): replace RunContext-based SequentialFlow with explici…
seyoung4503 Feb 23, 2026
fbbb1d7
refactor(components/retrieval): change KeywordRetriever to explicit s…
seyoung4503 Feb 23, 2026
125a8e1
test: update tests to RunContext-free API
seyoung4503 Feb 23, 2026
f7515ab
docs: update BaseFlow and RunContext docs to reflect RunContext-free API
seyoung4503 Feb 23, 2026
0a652fb
refactor(core): make run() the public hook-bearing entry point, _run(…
seyoung4503 Feb 24, 2026
41e8e46
feat(core): add LLMPort and DBPort protocols to ports.py
seyoung4503 Feb 24, 2026
3fdbc23
feat(components): add SQLGenerator component
seyoung4503 Feb 24, 2026
ac25b0e
feat(components): add SQLExecutor component
seyoung4503 Feb 24, 2026
6c61e91
feat(flows): add BaselineNL2SQL end-to-end pipeline
seyoung4503 Feb 24, 2026
df2effd
feat(integrations): add AnthropicLLM, OpenAILLM, and SQLAlchemyDB int…
seyoung4503 Feb 24, 2026
1f2575d
feat: expose public API in __init__.py and add optional extras to pyp…
seyoung4503 Feb 24, 2026
5000af1
test: add tests for SQLGenerator, SQLExecutor, and BaselineNL2SQL e2e…
seyoung4503 Feb 24, 2026
bcac584
feat(integrations/llm): add api_key parameter and fix IntegrationMiss…
seyoung4503 Feb 24, 2026
77fdad7
refactor(pyproject): promote anthropic and sqlalchemy to core depende…
seyoung4503 Feb 24, 2026
86d37a1
feat(generation): add db_dialect parameter with per-dialect prompt files
seyoung4503 Feb 24, 2026
8d965da
test(generation): add db_dialect tests for SQLGenerator
seyoung4503 Feb 24, 2026
87bcb4a
feat(scripts): add setup_sample_db.py for tutorial sample data
seyoung4503 Feb 24, 2026
140ea3f
docs: add quickstart.md tutorial for first-time users
seyoung4503 Feb 24, 2026
d5b9d59
feat(integrations/llm): add api_key param and fix IntegrationMissingE…
seyoung4503 Feb 24, 2026
17793c2
style: apply black formatting
seyoung4503 Feb 24, 2026
8d2f4ff
chore: upgrade black pre-commit hook to 26.1.0
seyoung4503 Feb 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
repos:
- repo: https://github.com/psf/black
rev: 25.1.0
rev: 26.1.0
hooks:
- id: black
91 changes: 26 additions & 65 deletions docs/BaseFlow_ko.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# BaseFlow

`BaseFlow`는 Lang2SQL에서 **define-by-run(순수 파이썬 제어)** 철학을 구현하기 위한 플로우의 최소 추상화(minimal abstraction)입니다.
`BaseFlow`는 Lang2SQL에서 **define-by-run(순수 파이썬 제어)** 철학을 구현하기 위한 "플로우의 최소 추상화(minimal abstraction)"입니다.

* 파이프라인의 **제어권(control-flow)** 을 프레임워크 DSL이 아니라 **사용자 코드(Python)** 가 갖습니다.
* LangGraph 같은 그래프 엔진을 강제하지 않습니다.
Expand All @@ -10,7 +10,7 @@

## 왜 필요한가?

### 1) 제어는 파이썬으로를 지키기 위해
### 1) "제어는 파이썬으로"를 지키기 위해

Text2SQL은 현실적으로 다음 제어가 자주 필요합니다.

Expand All @@ -19,11 +19,11 @@ Text2SQL은 현실적으로 다음 제어가 자주 필요합니다.
* 부분 파이프라인(서브플로우) 호출
* 정책(policy) 기반 행동 결정

`BaseFlow`는 이런 제어를 **사용자가 Python으로 직접 작성**하게 두고, 라이브러리는 실행 컨테이너 + 관측성만 제공합니다.
`BaseFlow`는 이런 제어를 **사용자가 Python으로 직접 작성**하게 두고, 라이브러리는 "실행 컨테이너 + 관측성"만 제공합니다.

### 2) 요청 단위 관측성(Flow-level tracing)

운영/디버깅에서는 이 요청 전체가 언제 시작했고, 어디서 실패했고, 얼마나 걸렸는지가 먼저 중요합니다.
운영/디버깅에서는 "이 요청 전체가 언제 시작했고, 어디서 실패했고, 얼마나 걸렸는지"가 먼저 중요합니다.

`BaseFlow`는 다음 이벤트를 발행합니다.

Expand All @@ -32,15 +32,6 @@ Text2SQL은 현실적으로 다음 제어가 자주 필요합니다.

→ 요청 1건을 **Flow 단위로 빠르게 파악**할 수 있습니다.

### 3) 공통 엔트리포인트(run_query) 제공

Text2SQL은 대부분 “문장(query)”이 시작점입니다.

`run_query("...")`를 제공하면:

* 초급 사용자는 `RunContext`를 몰라도 “바로 실행” 가능
* 고급 사용자는 `run(RunContext)`로 제어를 확장 가능

---

## BaseFlow가 제공하는 API
Expand All @@ -49,84 +40,57 @@ Text2SQL은 대부분 “문장(query)”이 시작점입니다.

```python
class MyFlow(BaseFlow):
def run(self, run: RunContext) -> RunContext:
def run(self, value):
...
return run
return result
```

* Flow의 본체 로직은 여기에 작성합니다.
* 제어는 Python으로 직접 작성합니다. (`if/for/while`)
* 입출력 타입은 자유롭게 정의합니다. 공유 상태 백(RunContext)을 강제하지 않습니다.

### 2) 호출: `__call__`

```python
out = flow(run)
out = flow(value)
```

* 내부적으로 `flow.run(...)`을 호출합니다.
* hook 이벤트를 `start/end/error`로 기록합니다.

### 3) 편의 엔트리포인트: `run_query()`

```python
out = flow.run_query("지난달 매출")
```

* 내부에서 `RunContext(query=...)`를 만들고 `run()`을 호출합니다.
* Quickstart / demo / 초급 UX용 엔트리포인트입니다.

> 권장: **BaseFlow에 run_query를 둬서 “모든 Flow는 run_query가 된다”는 직관을 유지**합니다.

---

## run(runcontext) vs run_query(query)

둘은 기능적으로 **같은 동작**을 하도록 설계합니다.

```python
out1 = flow.run_query("지난달 매출")
out2 = flow.run(RunContext(query="지난달 매출"))
```

* `run_query(query)` : 문자열 query에서 시작하는 편의 API
* `run(runcontext)` : 고급 사용자를 위한 명시적 API

---

## 사용 패턴

### 1) 초급: SequentialFlow로 구성하고 run_query로 실행
### 1) 초급: SequentialFlow로 구성하고 run으로 실행

초급 사용자는 보통 구성만 하고 실행하면 됩니다.
초급 사용자는 보통 "구성만 하고 실행"하면 됩니다.

```python
flow = SequentialFlow(steps=[retriever, builder, generator, validator])
out = flow.run_query("지난달 매출")
result = flow.run("지난달 매출")
```

각 step은 이전 step의 출력을 입력으로 받습니다.

### 2) 고급: CustomFlow로 제어(while/if/policy)

정책/루프/재시도 같은 제어가 들어오면 `BaseFlow`를 직접 상속해 작성하는 것이 가장 깔끔합니다.

```python
class RetryFlow(BaseFlow):
def run(self, run: RunContext) -> RunContext:
while True:
run = retriever(run)
metrics = eval_retrieval(run) # 순수 함수 가능
action = policy(metrics) # 순수 함수 가능
if action == "retry":
continue
break

run = generator(run)
run = validator(run)
return run
def run(self, query: str) -> str:
for _ in range(3):
schemas = retriever(query)
sql = generator(query, schemas)
if validator(sql):
return sql
return sql
```

### 3) Sequential을 유지하면서 동적 파라미터가 필요하면 closure/partial

이건 “필수”가 아니라, **steps 배열을 유지하고 싶은 사람을 위한 옵션**입니다.
이건 "필수"가 아니라, **steps 배열을 유지하고 싶은 사람을 위한 옵션**입니다.

---

Expand All @@ -140,7 +104,7 @@ from lang2sql.core.hooks import MemoryHook
hook = MemoryHook()
flow = SequentialFlow(steps=[...], hook=hook)

out = flow.run_query("지난달 매출")
result = flow.run("지난달 매출")

for e in hook.events:
print(e.name, e.phase, e.component, e.duration_ms, e.error)
Expand All @@ -153,8 +117,8 @@ for e in hook.events:

## (관련 개념) BaseFlow와 BaseComponent의 관계

* `BaseFlow`는 어떻게 실행할지(제어/조립)를 담당합니다.
* `BaseComponent`는 한 단계에서 무엇을 할지(작업 단위)를 담당합니다.
* `BaseFlow`는 "어떻게 실행할지(제어/조립)"를 담당합니다.
* `BaseComponent`는 "한 단계에서 무엇을 할지(작업 단위)"를 담당합니다.

일반적으로:

Expand All @@ -176,8 +140,5 @@ A. Flow라는 개념은 사실상 필요하지만, **모든 사용자가 BaseFlo

### Q. Flow의 반환 타입은?

A. `run()`은 **반드시 `RunContext`를 반환**하는 것을 권장합니다.
(합성/디버깅/타입 안정성 측면에서 이득이 큽니다.)

---

A. `run()`의 입출력 타입은 자유롭습니다. 컴포넌트끼리 합의한 타입을 그대로 사용하면 됩니다.
`SequentialFlow`는 각 step의 출력을 다음 step의 입력으로 전달하는 파이프입니다.
47 changes: 33 additions & 14 deletions docs/RunContext_ko.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
> **레거시 유틸리티**: `RunContext`는 현재 레거시 유틸리티로 유지됩니다.
> 새 코드에서는 명시적 Python 인자를 사용하는 것을 권장합니다.
> 컴포넌트 I/O는 `RunContext` 대신 구체적인 타입(str, list 등)으로 표현하세요.

## RunContext

`RunContext`는 define-by-run 파이프라인에서 **상태(state)를 운반하는 최소 State Carrier**입니다.
컴포넌트는 기본적으로 `RunContext -> RunContext` 계약을 따르며, 필요한 값을 읽고/쓰면서 파이프라인을 구성합니다.
`RunContext`는 define-by-run 파이프라인에서 **상태(state)를 운반하는 State Carrier**입니다.
레거시 파이프라인이나 직접 상태를 조합할 때 유용합니다.

### 설계 원칙

Expand All @@ -13,7 +17,7 @@

## 데이터 구조 트리

아래는 `RunContext`가 담는 데이터 구조를 트리 형태로 나타낸 것입니다.
아래는 `RunContext`가 담는 데이터 구조를 "트리 형태"로 나타낸 것입니다.

```
RunContext
Expand Down Expand Up @@ -93,21 +97,36 @@ RunContext

---

## 파이프라인 예시 (Text2SQL)
## 파이프라인 예시 (Text2SQL) — 새 API

새 API에서는 각 컴포넌트가 명시적 인자를 주고받습니다.

개념:
```python
query = "지난달 매출"

* retriever: `(query, catalog) -> selected`
* builder: `(query, selected) -> context`
* generator: `(query, context) -> sql`
* validator: `(sql) -> validation`
schemas = retriever(query) # str → list[CatalogEntry]
context = builder(query, schemas) # str, list → str
sql = generator(query, context) # str, str → str
result = validator(sql) # str → ValidationResult
```

RunContext에서의 읽기/쓰기:
또는 `SequentialFlow`로 조합:

* retriever: `run.query`, `run.schema_catalog` 읽고 → `run.schema_selected` 작성
* builder: `run.query`, `run.schema_selected` 읽고 → `run.schema_context` 작성
* generator: `run.query`, `run.schema_context` 읽고 → `run.sql` 작성
* validator: `run.sql` 읽고 → `run.validation` 작성
```python
flow = SequentialFlow(steps=[retriever, builder, generator, validator])
result = flow.run(query)
```

---

## RunContext 직접 사용 (레거시)

기존 코드나 직접 상태를 조합할 때만 사용합니다.

```python
from lang2sql.core.context import RunContext

run = RunContext(query="지난달 매출")
# run을 직접 조작하거나 레거시 컴포넌트에 전달
run.metadata["session_id"] = "abc123"
```
6 changes: 2 additions & 4 deletions interface/app_pages/chatbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,13 @@ def initialize_session_state():
# 페이지 제목
st.title("🤖 AI ChatBot")

st.markdown(
"""
st.markdown("""
LangGraph 기반 AI ChatBot과 대화를 나눌 수 있습니다.
- 데이터베이스 테이블 정보 검색
- 용어집 조회
- 쿼리 예제 조회
- 대화를 통해 질문 구체화
"""
)
""")

# 설정 로드
config = load_config()
Expand Down
6 changes: 2 additions & 4 deletions interface/app_pages/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@

st.title("🏠 홈")

st.markdown(
"""
st.markdown("""
### Lang2SQL 데이터 분석 도구에 오신 것을 환영합니다 🎉

이 도구는 자연어로 작성한 질문을 SQL 쿼리로 변환하고,
Expand All @@ -21,7 +20,6 @@
2. **🔍 Lang2SQL**: 자연어 → SQL 변환 및 결과 분석
3. **📊 그래프 빌더**: LangGraph 실행 순서를 프리셋/커스텀으로 구성하고 세션에 적용
4. **⚙️ 설정**: 데이터 소스, LLM, DB 연결 등 환경 설정
"""
)
""")

st.info("왼쪽 메뉴에서 기능 페이지를 선택해 시작하세요 🚀")
1 change: 0 additions & 1 deletion interface/app_pages/lang2sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
render_sidebar_db_selector,
)


TITLE = "Lang2SQL"
DEFAULT_QUERY = "고객 데이터를 기반으로 유니크한 유저 수를 카운트하는 쿼리"
SIDEBAR_OPTIONS = {
Expand Down
1 change: 0 additions & 1 deletion interface/app_pages/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from interface.app_pages.settings_sections.llm_section import render_llm_section
from interface.app_pages.settings_sections.db_section import render_db_section


st.title("⚙️ 설정")

config = load_config()
Expand Down
1 change: 0 additions & 1 deletion interface/app_pages/settings_sections/llm_section.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
get_embedding_registry,
)


LLM_PROVIDERS = [
"openai",
"azure",
Expand Down
6 changes: 2 additions & 4 deletions interface/core/result_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,11 @@ def should_show(_key: str) -> bool:
st.markdown("---")
token_summary = TokenUtils.get_token_usage_summary(data=res["messages"])
st.write("**토큰 사용량:**")
st.markdown(
f"""
st.markdown(f"""
- Input tokens: `{token_summary['input_tokens']}`
- Output tokens: `{token_summary['output_tokens']}`
- Total tokens: `{token_summary['total_tokens']}`
"""
)
""")

if show_sql_section:
st.markdown("---")
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ dependencies = [
"pgvector==0.3.6",
"langchain-postgres==0.0.15",
"trino>=0.329.0,<1.0.0",
"anthropic>=0.20.0",
"sqlalchemy>=2.0",
]

[project.scripts]
Expand Down
Loading