code-agent: implement issue 4
Approval-Correlation: 0543bb321a84cb936bc0ac4988615840
Plan-SHA256: 437a85f68d151ed4db780511812834041cfec5e7cf021fb77e238d5a6196e6fb
Base-SHA: 256be64ac7
This commit is contained in:
+33
-7
@@ -1,13 +1,39 @@
|
||||
# 데이터 모델
|
||||
|
||||
## input-display-state
|
||||
## 대화 상태
|
||||
|
||||
초기 구현의 상태는 `ChatApp.submitted_inputs`와 화면 위젯으로 구성한다.
|
||||
`ChatApp`은 세션 동안 다음 목록을 메모리에 유지한다.
|
||||
|
||||
- `Input.value`: 현재 입력창에 편집 중인 문자열이다.
|
||||
- `submitted_inputs`: 현재 애플리케이션 세션에서 제출된 문자열의 순서 있는 목록이다.
|
||||
- `#conversation`: 제출된 각 문자열을 `Label`로 표시하는 세션 내 대화 영역이다.
|
||||
- `submitted_inputs`: `trim`된 유효 사용자 메시지 목록
|
||||
- `selected_responses`: 각 사용자 메시지에 대응하는 선택 응답 목록
|
||||
|
||||
입력 제출이 성공하면 원문을 `submitted_inputs`에 추가하고 같은 문자열을 대화 영역에 표시한 뒤 입력창을 비운다. 빈 문자열은 추가하지 않는다.
|
||||
두 목록은 같은 인덱스를 사용하며, 유효한 제출 하나마다 항목 하나씩 추가된다. 공백 입력은 어느 목록도 변경하지 않는다.
|
||||
|
||||
이 모델은 프로세스 메모리에만 존재한다. 파일, 데이터베이스, 외부 서비스에는 기록하지 않으며 애플리케이션 종료 시 상태가 폐기된다. LLM 응답이나 메시지 작성자 구분은 초기 범위에 포함하지 않는다.
|
||||
## 고정 응답 목록
|
||||
|
||||
`src/chat_tui/app.py`의 `RESPONSE_MESSAGES` 상수는 다음 다섯 문자열을 순서대로 보유한다.
|
||||
|
||||
1. `안녕하세요! 무엇을 도와드릴까요?`
|
||||
2. `그것은 제가 처리하기 불가능합니다.`
|
||||
3. `맡겨만 주세요.`
|
||||
4. `지금 처리 중입니다.`
|
||||
5. `작업을 완료하였습니다.`
|
||||
|
||||
응답은 `random.choice(RESPONSE_MESSAGES)`로 선택한다. 난수 선택 자체는 테스트에서 `chat_tui.app.random.choice`를 대체할 수 있는 경계로 취급한다.
|
||||
|
||||
## 대화 항목
|
||||
|
||||
유효한 입력이 제출되면 대화 영역에 다음 두 `Label`을 순서대로 추가한다.
|
||||
|
||||
1. `user-message` 클래스의 사용자 메시지
|
||||
2. `response-message` 클래스의 선택 응답
|
||||
|
||||
사용자 메시지는 입력값을 `strip()`한 결과이며, 선택 응답은 고정 응답 목록의 문자열이다.
|
||||
|
||||
## 불변 조건
|
||||
|
||||
- 공백 제거 결과가 빈 문자열이면 상태와 화면을 변경하지 않는다.
|
||||
- 유효한 입력마다 사용자 메시지가 응답보다 먼저 추가된다.
|
||||
- `submitted_inputs`와 `selected_responses`의 길이는 항상 같다.
|
||||
- 응답은 고정 목록에 포함된 값이어야 한다.
|
||||
- 대화 기록은 애플리케이션 세션 안에서만 유지한다.
|
||||
|
||||
+28
-14
@@ -1,24 +1,38 @@
|
||||
# 인터페이스 계약
|
||||
|
||||
## tui-input-display
|
||||
## 메시지 제출 이벤트
|
||||
|
||||
### 화면
|
||||
Textual의 `Input.Submitted` 이벤트가 `#user-input`에서 발생하면 `ChatApp.on_input_submitted`가 처리한다.
|
||||
|
||||
- Textual 애플리케이션은 헤더, `#conversation` 대화 영역, `#user-input` 입력창을 제공한다.
|
||||
- 애플리케이션 시작 시 입력창에 포커스를 둔다.
|
||||
### 입력 정규화
|
||||
|
||||
### 입력 제출
|
||||
1. 이벤트의 `value`에 `strip()`을 적용한다.
|
||||
2. 입력 위젯의 값을 빈 문자열로 초기화한다.
|
||||
3. 정규화 결과가 빈 문자열이면 추가 처리를 중단한다.
|
||||
|
||||
- 사용자가 `#user-input`에서 Enter를 누르면 현재 값이 제출된다.
|
||||
- 비어 있지 않은 값은 변경 없이 대화 영역에 새 항목으로 추가된다.
|
||||
- 제출 후 입력창은 빈 값이 된다.
|
||||
- 대화 영역의 항목 순서는 제출 순서와 같다.
|
||||
공백만 포함한 입력은 사용자 메시지, 응답 선택, 대화 항목을 생성하지 않는다.
|
||||
|
||||
### 종료
|
||||
### 유효한 입력 처리
|
||||
|
||||
- `Escape`와 `Ctrl+Q`는 애플리케이션의 종료 동작을 호출한다.
|
||||
- 종료 시 세션 내 메모리 상태를 영구 저장하지 않는다.
|
||||
정규화 결과가 비어 있지 않으면 다음 순서로 처리한다.
|
||||
|
||||
### 경계
|
||||
1. 정규화된 사용자 메시지를 `submitted_inputs`에 추가한다.
|
||||
2. `random.choice(RESPONSE_MESSAGES)`로 응답을 선택한다.
|
||||
3. 선택 응답을 `selected_responses`에 추가한다.
|
||||
4. 대화 영역에 사용자 메시지 `Label`을 추가한다.
|
||||
5. 바로 이어서 응답 `Label`을 추가한다.
|
||||
6. 대화 영역을 마지막 항목까지 스크롤한다.
|
||||
|
||||
이 인터페이스는 화면 표시와 입력 상태 관리만 담당한다. LLM 클라이언트, 네트워크 요청, 인증 및 외부 서비스 연동은 제공하지 않는다.
|
||||
사용자 메시지와 응답은 별도 대화 항목이며, 화면 표시 순서는 항상 사용자 메시지 다음 응답이다.
|
||||
|
||||
## 화면 위젯 계약
|
||||
|
||||
- 대화 영역은 `#conversation` 식별자의 `VerticalScroll`이다.
|
||||
- 입력창은 `#user-input` 식별자의 `Input`이다.
|
||||
- 사용자 메시지는 `user-message` 클래스를 사용한다.
|
||||
- 응답은 `response-message` 클래스를 사용한다.
|
||||
- 애플리케이션 마운트 시 입력창에 포커스를 둔다.
|
||||
|
||||
## 종료 계약
|
||||
|
||||
`Escape`와 `Ctrl+Q`는 기존 `quit` 바인딩을 통해 애플리케이션 종료를 요청한다.
|
||||
|
||||
+29
-20
@@ -1,23 +1,32 @@
|
||||
# 프로젝트 구조
|
||||
|
||||
```text
|
||||
.
|
||||
├── pyproject.toml
|
||||
├── README.md
|
||||
├── src/
|
||||
│ └── chat_tui/
|
||||
│ ├── __init__.py
|
||||
│ ├── __main__.py
|
||||
│ └── app.py
|
||||
├── tests/
|
||||
│ └── test_app.py
|
||||
└── docs/
|
||||
├── data-model.md
|
||||
├── interface-contract.md
|
||||
├── project-structure.md
|
||||
├── requirements/
|
||||
│ └── req-001_project-scaffold.md
|
||||
└── runtime-flow.md
|
||||
```
|
||||
## 애플리케이션 구현
|
||||
|
||||
`pyproject.toml`은 Python 3.11 이상, Textual 런타임 의존성, 선택적 테스트 의존성, 콘솔 진입점을 선언한다. `src/chat_tui/app.py`는 Textual 애플리케이션과 실행 함수를 제공하고, `tests/test_app.py`는 입력 표시와 종료 계약을 검증한다.
|
||||
- `src/chat_tui/app.py`
|
||||
- `RESPONSE_MESSAGES`: 수정하지 않는 고정 응답 목록
|
||||
- `ChatApp.on_input_submitted`: 입력 `trim`, 유효성 판정, 난수 응답 선택, 상태 갱신 및 대화 항목 표시
|
||||
- `ChatApp.submitted_inputs`: 정규화된 사용자 메시지 세션 상태
|
||||
- `ChatApp.selected_responses`: 메시지별 선택 응답 세션 상태
|
||||
- 기존 Textual 위젯 구성과 `Escape`, `Ctrl+Q` 종료 바인딩
|
||||
|
||||
응답 기능은 별도 서비스나 외부 모듈을 만들지 않고 현재 단일 TUI 애플리케이션의 제출 이벤트 처리에 배치한다. 난수 선택은 표준 라이브러리 `random.choice`를 직접 사용한다.
|
||||
|
||||
## 테스트
|
||||
|
||||
- `tests/test_app.py`
|
||||
- 사용자 메시지와 응답의 제출 순서 및 화면 순서
|
||||
- `random.choice` 결과 제어와 호출 인자
|
||||
- `trim`된 입력 저장
|
||||
- 공백 입력 무시
|
||||
- 입력창 초기화
|
||||
- `Escape` 종료 요청
|
||||
|
||||
테스트는 Textual의 `run_test()`를 사용해 실제 위젯 트리와 이벤트 흐름을 검증하고, 난수 선택만 `unittest.mock.patch`로 제어한다.
|
||||
|
||||
## 문서
|
||||
|
||||
- `docs/requirements/req-001_project-scaffold.md`: 기능 범위와 수용 기준
|
||||
- `docs/data-model.md`: 응답 목록과 세션 상태 모델
|
||||
- `docs/interface-contract.md`: 제출 이벤트와 화면 표시 계약
|
||||
- `docs/project-structure.md`: 기능 구성 요소와 책임
|
||||
- `docs/runtime-flow.md`: 실행 시나리오와 처리 순서
|
||||
|
||||
@@ -8,19 +8,37 @@ Python 3.11 이상과 표준 `pyproject.toml`을 사용하는 Textual 기반 TUI
|
||||
|
||||
1. `chat-tui` 콘솔 명령과 `python -m chat_tui` 실행을 지원한다.
|
||||
2. Textual 화면에 사용자 입력창과 대화 영역을 표시한다.
|
||||
3. 사용자가 입력을 제출하면 입력한 문자열을 세션 내 대화 영역에 순서대로 누적한다.
|
||||
4. 제출 뒤 입력창을 비운다.
|
||||
5. `Escape` 또는 `Ctrl+Q`로 애플리케이션 종료를 요청할 수 있다.
|
||||
6. 입력 표시와 종료 동작을 자동 테스트로 검증한다.
|
||||
3. 사용자가 입력을 제출하면 입력값을 `trim`한 뒤 비어 있지 않은 사용자 메시지를 제출 순서대로 대화 영역에 누적한다.
|
||||
4. 유효한 사용자 메시지 제출 후 고정된 다섯 개 응답 목록에서 표준 난수 선택으로 하나의 응답을 결정한다.
|
||||
5. 선택한 응답을 해당 사용자 메시지 바로 뒤에 별도의 대화 항목으로 표시한다.
|
||||
6. 유효한 입력 처리 뒤 입력창을 비운다.
|
||||
7. 빈 입력 또는 공백만 포함한 입력은 사용자 메시지와 응답을 대화 기록에 추가하지 않는다.
|
||||
8. `Escape` 또는 `Ctrl+Q`로 애플리케이션 종료를 요청할 수 있다.
|
||||
9. 입력 누적, 응답 선택과 표시, 공백 입력 무시, 입력창 초기화 및 종료 동작을 자동 테스트로 검증한다.
|
||||
|
||||
## 고정 응답 목록
|
||||
|
||||
응답은 다음 다섯 문구 중 하나를 사용한다.
|
||||
|
||||
- 안녕하세요! 무엇을 도와드릴까요?
|
||||
- 그것은 제가 처리하기 불가능합니다.
|
||||
- 맡겨만 주세요.
|
||||
- 지금 처리 중입니다.
|
||||
- 작업을 완료하였습니다.
|
||||
|
||||
응답 선택에는 표준 라이브러리의 난수 선택 기능을 사용하며, 외부 서비스나 LLM은 호출하지 않는다.
|
||||
|
||||
## 제외 범위
|
||||
|
||||
LLM 연동, 응답 생성, 외부 API 호출, 인증, 영구 대화 기록 저장은 구현하지 않는다.
|
||||
LLM 연동, 외부 API 호출, 인증, 영구 대화 기록 저장, 사용자별 응답 기록, 응답 목록 편집, 사용자 입력에 따른 맞춤형 응답 생성은 구현하지 않는다.
|
||||
|
||||
## 수용 기준
|
||||
|
||||
- Python 3.11 이상 환경에서 의존성을 설치할 수 있다.
|
||||
- TUI가 실행되고 입력창에 포커스가 배치된다.
|
||||
- 두 번 이상 제출한 입력이 제출 순서대로 모두 표시된다.
|
||||
- 빈 입력은 대화 기록에 추가되지 않는다.
|
||||
- 자동 테스트가 입력 누적 표시와 종료 요청을 검증한다.
|
||||
- 두 번 이상 제출한 입력이 `trim`된 값으로 제출 순서대로 표시된다.
|
||||
- 각 유효한 사용자 메시지 바로 뒤에 고정 목록에서 선택된 응답이 표시된다.
|
||||
- 자동 테스트에서 난수 선택 결과를 제어하고 선택 호출 및 표시 순서를 검증할 수 있다.
|
||||
- 빈 입력과 공백만 포함한 입력은 사용자 메시지, 응답 및 응답 선택 결과를 남기지 않는다.
|
||||
- 유효한 입력 제출 뒤 입력창이 비워진다.
|
||||
- `Escape` 및 `Ctrl+Q`가 애플리케이션 종료를 요청한다.
|
||||
|
||||
+36
-10
@@ -1,13 +1,39 @@
|
||||
# 실행 흐름
|
||||
# 런타임 흐름
|
||||
|
||||
## tui-input-loop
|
||||
## 정상 입력 시나리오
|
||||
|
||||
1. `chat-tui` 또는 `python -m chat_tui`가 `main()`을 호출한다.
|
||||
2. `ChatApp`이 Textual 이벤트 루프를 시작하고 화면을 구성한다.
|
||||
3. 입력창에 포커스를 둔 뒤 사용자의 입력을 기다린다.
|
||||
4. 사용자가 Enter를 누르면 `Input.Submitted` 이벤트를 처리한다.
|
||||
5. 비어 있지 않은 입력을 세션 목록에 추가하고 같은 값을 대화 영역의 새 `Label`로 마운트한다.
|
||||
6. 입력창을 비우고 대화 영역을 마지막 항목으로 스크롤한 뒤 다시 입력을 기다린다.
|
||||
7. 사용자가 `Escape` 또는 `Ctrl+Q`를 누르면 Textual 종료 동작을 호출한다.
|
||||
1. 애플리케이션이 `#user-input`에 포커스를 둔다.
|
||||
2. 사용자가 입력하고 Enter를 누른다.
|
||||
3. `Input.Submitted` 이벤트에서 입력값을 `strip()`한다.
|
||||
4. 입력창 값을 즉시 빈 문자열로 초기화한다.
|
||||
5. `trim` 결과가 유효하면 사용자 메시지를 세션 상태에 기록한다.
|
||||
6. `random.choice(RESPONSE_MESSAGES)`가 고정 응답 하나를 선택한다.
|
||||
7. 선택 응답을 세션 상태에 기록한다.
|
||||
8. 대화 영역에 사용자 메시지 `Label`을 추가한다.
|
||||
9. 사용자 메시지 바로 뒤에 응답 `Label`을 추가한다.
|
||||
10. 대화 영역을 마지막 항목으로 스크롤한다.
|
||||
|
||||
이 흐름에는 LLM 호출이나 외부 네트워크 요청이 없다. 애플리케이션 종료 뒤 세션 목록과 화면 항목은 보존되지 않는다.
|
||||
여러 번 제출하면 각 사용자 메시지와 응답 쌍이 제출 순서대로 누적된다.
|
||||
|
||||
## 공백 입력 시나리오
|
||||
|
||||
1. 사용자가 공백만 포함한 값을 제출한다.
|
||||
2. 입력값을 `strip()`한다.
|
||||
3. 결과가 빈 문자열임을 확인한다.
|
||||
4. 입력창은 비우되 상태, 난수 선택 결과 및 대화 영역은 변경하지 않는다.
|
||||
|
||||
## 종료 시나리오
|
||||
|
||||
- `Escape`를 누르면 기존 종료 바인딩이 실행된다.
|
||||
- `Ctrl+Q`를 누르면 기존 종료 바인딩이 실행된다.
|
||||
- 두 동작 모두 `quit` 액션을 통해 애플리케이션 종료를 요청한다.
|
||||
|
||||
## 검증 시나리오
|
||||
|
||||
자동 테스트는 결정 가능한 `random.choice` 결과를 사용해 다음을 확인한다.
|
||||
|
||||
- 입력 `trim` 결과가 사용자 메시지로 저장되고 표시되는지
|
||||
- 사용자 메시지 다음에 응답이 표시되는지
|
||||
- 공백 입력에서 `random.choice`가 호출되지 않는지
|
||||
- 입력창이 제출 후 비워지는지
|
||||
- 기존 종료 동작이 유지되는지
|
||||
|
||||
Reference in New Issue
Block a user