Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 17 additions & 15 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
| D-7 (RagClient) | `packages/agent/src/tools/rag-client.ts` |
| D-8 (.md frontmatter) | `packages/agent/src/tools/prompt-loader.ts`, `packages/agent/prompts/*.md` |
| D-9 (Next.js 14 + 듀얼-서피스 디자인) | `packages/web/`, `packages/web/DESIGN.md` |
| D-11 (I-G4 투명 노출 정책) | `packages/agent/src/server/routes/generate.ts`, `packages/web/DESIGN.md` `{component.result-card-failed}` |
| D-12 (S0-B OCR v2 보류) | `packages/web/` S0 카드 disabled 상태 |
| [`docs/specs/domain.md`](docs/specs/domain.md) §2 도메인 | `packages/agent/src/schemas/*.schema.ts` |
| 불변식 I-V1 ~ I-V5 | `assertVerificationInvariants` |
| 불변식 I-I1 ~ I-I3 | `IntentSchema.regex` + `assertIntentInvariants` |
Expand All @@ -42,9 +44,9 @@
|---|---|
| 의존성 설치 + git hooks | `pnpm install` |
| 세 서비스 동시 dev | `pnpm dev:all` |
| agent만 dev | `pnpm dev` (`:3000`) |
| math-engine만 dev | `pnpm dev:math` (`:8000`) |
| web만 dev | `pnpm dev:web` (`:3001`) |
| agent만 dev | `pnpm dev` (`:31415`) |
| math-engine만 dev | `pnpm dev:math` (`:16180`) |
| web만 dev | `pnpm dev:web` (`:27182`) |
| Node typecheck (agent + web) | `pnpm typecheck` |
| Node unit test (agent) | `pnpm -F @openmath/agent test` |
| Node integration test | `pnpm -F @openmath/agent test:integration` |
Expand Down Expand Up @@ -147,29 +149,29 @@ agent 코드를 *건드리지 않고* 동작이 바뀌는 영역.

---

## 9. 진입점 우선순위 (현재 미구현)
## 9. 진입점 현황 (구현 완료)

`grep -rn "not implemented yet" packages/agent/src` 로 14곳 확인 가능.
`grep -rn "not implemented yet" packages/agent/src` 로 0곳 — 아래 진입점 모두 구현 완료.

| Priority | 파일 | 비고 |
|---|---|---|
| P0 | `tools/math-engine-client.ts` `createMathEngineClient` | math-engine은 작동 중. 첫 통합 |
| P0 | `tools/prompt-loader.ts` `createFsPromptLoader` | gray-matter + handlebars |
| P0 | `tools/llm-provider.ts` `resolveLanguageModel` | CLIProxyAPI 우선 |
| P1 | `tools/rag-client.ts` `createInMemoryRagClient` | JSONL + filter |
| P1 | `agents/*-agent.ts` (4개) | `generateObject` + prompt 로드 |
| P1 | `steps/*.ts` (6개) | agents/tools 얇은 wrapper |
| P2 | `workflows/verification-workflow.ts` | async generator + I-V invariant assert |
| P2 | `server/routes/generate.ts` + `sse/progress-stream.ts` | streamSSE 흘리기 |
| P3 | `src/index.ts` `main` | dep wiring (10줄) |
| P0 | `tools/math-engine-client.ts` `createMathEngineClient` | 구현 완료 |
| P0 | `tools/prompt-loader.ts` `createFsPromptLoader` | 구현 완료 (gray-matter + handlebars) |
| P0 | `tools/llm-provider.ts` `resolveLanguageModel` | 구현 완료 (CLIProxyAPI 우선) |
| P1 | `tools/rag-client.ts` `createInMemoryRagClient` | 구현 완료 (JSONL + filter) |
| P1 | `agents/*-agent.ts` (4개) | 구현 완료 |
| P1 | `steps/*.ts` (6개) | 구현 완료 |
| P2 | `workflows/verification-workflow.ts` | 구현 완료 |
| P2 | `server/routes/generate.ts` + `sse/progress-stream.ts` | 구현 완료 |
| P3 | `src/index.ts` `main` | 구현 완료 |

---

## 10. 관련 문서 index

- [`README.md`](README.md) — 프로젝트 무엇·왜·누구
- [`CONTRIBUTING.md`](CONTRIBUTING.md) — 브랜치·hook·커밋·CI 규칙
- [`docs/specs/architecture.md`](docs/specs/architecture.md) — L0 시스템 경계, 결정 8개, Open Questions 4개
- [`docs/specs/architecture.md`](docs/specs/architecture.md) — L0 시스템 경계, 결정 12개, Open Questions 4개
- [`docs/specs/domain.md`](docs/specs/domain.md) — L1 도메인 개념 + 불변식 17개
- [`docs/product/`](docs/product/) — 사용자 측 기획 5종 + HTML preview (2026-05-07 핸드오프)
- [`docs/PROGRESS.md`](docs/PROGRESS.md) — 데이터 파이프라인 + 에이전트 실험 보고 (2026-04-03)
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ LLM access is pluggable: direct OpenAI/Anthropic, or via [CLIProxyAPI](https://g

## Status

- **`math-engine`** — operational. 5 endpoints (`/solve`, `/verify`, `/simplify`, `/differentiate`, `/limit`) + 19 pytest passing.
- **`agent`** — scaffolded per spec; 42 TS files with stable interfaces + Zod schemas. Implementations pending (search `throw new Error(".*: not implemented yet")`).
- **`web`** — Landing page + DESIGN.md spec scaffolded. `pnpm build` green, static prerender. 추가 화면 (S0~S6) 마이그레이션은 FE 담당.
- **L0 architecture** — Proposed (D-1 ~ D-9). **L1 domain** — Draft. L2 contracts·L3 modules: TBD.
- **`math-engine`** — operational. 5 endpoints (`/solve`, `/verify`, `/simplify`, `/differentiate`, `/limit`) + 20 pytest passing.
- **`agent`** — implemented per spec; 48 TS source files with stable interfaces + Zod schemas. No stubs remain (`grep "not implemented yet" packages/agent/src` returns 0 matches).
- **`web`** — Landing + S0~S6 workflow screens + DESIGN.md spec implemented. `pnpm build` green. S5/S6 (결과·출력)은 현재 mock 데이터 기반 — agent SSE 연동 진행 중.
- **L0 architecture** — Proposed (D-1 ~ D-12). **L1 domain** — Draft. L2 contracts·L3 modules: TBD.

## Development

Expand All @@ -55,9 +55,9 @@ pnpm typecheck # tsc --noEmit on agent + web
pnpm build # production build (agent + web)
```

- Agent: `http://localhost:3000` (SSE at `POST /api/generate`)
- Math Engine: `http://localhost:8000`
- Web: `http://localhost:3001` (landing)
- Agent: `http://localhost:31415` (SSE at `POST /api/generate`)
- Math Engine: `http://localhost:16180`
- Web: `http://localhost:27182` (landing)

### LLM 환경 설정 (필수)

Expand Down Expand Up @@ -101,11 +101,11 @@ LLM_MODEL=claude-3-5-sonnet-20241022

### 포트 충돌 회피

다른 프로세스(Mantis, 별도 Next.js 서버 등)가 3000번을 점유하면 `pnpm dev:all` 시 agent가 `EADDRINUSE`로 실패. 다음 단계로 회피:
다른 프로세스(Mantis, 별도 Next.js 서버 등)가 31415번을 점유하면 `pnpm dev:all` 시 agent가 `EADDRINUSE`로 실패. 다음 단계로 회피:

```bash
# 1. 충돌 확인
lsof -i :3000
lsof -i :31415

# 2. agent 포트 변경
PORT=3002 pnpm dev
Expand All @@ -118,7 +118,7 @@ echo "NEXT_PUBLIC_AGENT_URL=http://localhost:3002" > packages/web/.env.local
pnpm -F @openmath/web dev
```

`NEXT_PUBLIC_AGENT_URL`이 미설정이면 web은 `http://localhost:3000`을 호출하므로 포트 변경 시 반드시 `.env.local` 갱신.
`NEXT_PUBLIC_AGENT_URL`이 미설정이면 web은 `http://localhost:31415`를 호출하므로 포트 변경 시 반드시 `.env.local` 갱신.

### 데모 실행 절차 (캡스톤)

Expand All @@ -133,7 +133,7 @@ cp packages/agent/.env.example packages/agent/.env
# 2. (매 시연) 세 서비스 기동
pnpm dev:all

# 3. (브라우저) http://localhost:3001 → S0 → 학년(중3) → 단원(이차방정식)
# 3. (브라우저) http://localhost:27182 → S0 → 학년(중3) → 단원(이차방정식)
# → 평가 차원(인수분해 또는 근의 공식 사용 + 판별식) → 생성

# 4. (검증) S4 6단계 ✓ → S5 결과 3문항 → S6 PDF
Expand All @@ -142,7 +142,7 @@ pnpm dev:all
또는 직접 SSE:

```bash
curl -sN -X POST http://localhost:3000/api/generate \
curl -sN -X POST http://localhost:31415/api/generate \
-H "Content-Type: application/json" \
-d '{"grade":3,"topic":"9수02-09","mode":"structural","dims":["인수분해 또는 근의 공식 사용","판별식으로 해의 종류 해석"]}'
```
Expand All @@ -163,7 +163,7 @@ packages/
│ └── data/ # corpus JSONL + strategy YAML — hand-off slot
├── math-engine/ # Python — SymPy verification HTTP
│ ├── src/ # FastAPI app + routers
│ └── tests/ # pytest (19 tests)
│ └── tests/ # pytest (20 tests)
└── web/ # Node 22 — Next.js 14 App Router + Tailwind v4
├── DESIGN.md # 디자인 시스템 spec (editorial + productivity 듀얼-서피스)
├── app/
Expand Down
4 changes: 2 additions & 2 deletions docs/specs/rag-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ CORPUS_JSONL=/absolute/path/to/rag_problem_generation_dataset/openmath_rag_recor
예시 로컬 `.env`:

```env
PORT=3000
MATH_ENGINE_URL=http://localhost:8000
PORT=31415
MATH_ENGINE_URL=http://localhost:16180
CORPUS_JSONL=/Users/<name>/dev/AI_HUB_data/rag_problem_generation_dataset/openmath_rag_records.jsonl
```

Expand Down
6 changes: 3 additions & 3 deletions packages/agent/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
# 모두 optional. 미설정 시 src/config/env.ts 기본값 또는 src/index.ts fallback 사용.

# --- 서버 ---
# 다른 프로세스(예: Mantis 등)가 3000 점유 시 PORT 변경 + 동시에
# 기본 31415 (π). 다른 프로세스 점유 시 PORT 변경 + 동시에
# packages/web/.env.local 의 NEXT_PUBLIC_AGENT_URL 도 같이 갱신해야 web↔agent 연결됨.
PORT=3000
MATH_ENGINE_URL=http://localhost:8000
PORT=31415
MATH_ENGINE_URL=http://localhost:16180

# --- RAG ---
# 미설정 시 ./data/corpus/math-sample-unified-v1.jsonl (repo 동봉) 사용.
Expand Down
4 changes: 2 additions & 2 deletions packages/agent/src/config/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import { z } from "zod";

export const EnvSchema = z.object({
NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
PORT: z.coerce.number().int().min(1).max(65535).default(3000),
PORT: z.coerce.number().int().min(1).max(65535).default(31415),

MATH_ENGINE_URL: z.string().url().default("http://localhost:8000"),
MATH_ENGINE_URL: z.string().url().default("http://localhost:16180"),

LLM_PROVIDER: z
.enum(["openai", "openai-compatible", "anthropic-via-compatible", "cliproxy"])
Expand Down
2 changes: 1 addition & 1 deletion packages/agent/src/server/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function createApp(deps: AppDeps): Hono {
app.use(
"*",
cors({
origin: ["http://localhost:3001", "http://127.0.0.1:3001"],
origin: ["http://localhost:27182", "http://127.0.0.1:27182"],
allowHeaders: ["Content-Type", "Accept"],
allowMethods: ["GET", "POST", "OPTIONS"],
}),
Expand Down
2 changes: 1 addition & 1 deletion packages/math-engine/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
uvicorn.run(
"src.main:app",
host="0.0.0.0",
port=8000,
port=16180,
reload=True,
)
19 changes: 10 additions & 9 deletions packages/web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@

OpenMath 프론트엔드 (Next.js 14 App Router + Tailwind v4).

랜딩 페이지 보일러플레이트 + [DESIGN.md](./DESIGN.md) 디자인 시스템 spec. 1차 핸드오프 시점에는 `/` 랜딩 1 화면만 작동.
랜딩 페이지 보일러플레이트 + [DESIGN.md](./DESIGN.md) 디자인 시스템 spec. 현재 `/`, `/login`, `/samples`, `/app/*` (S0~S6) 가 작동.

---

## 빠른 시작

```bash
pnpm install # 루트에서 1회
pnpm -F @openmath/web dev # http://localhost:3001
pnpm -F @openmath/web dev # http://localhost:27182
```

`agent` 서비스 (포트 3000) 가 동시에 떠 있어야 SSE 엔드포인트를 호출할 수 있다. `pnpm dev:all` 사용 권장.
`agent` 서비스 (포트 31415) 가 동시에 떠 있어야 SSE 엔드포인트를 호출할 수 있다. `pnpm dev:all` 사용 권장.

## 디렉토리

Expand Down Expand Up @@ -54,25 +54,26 @@ npx @google/design.md lint DESIGN.md

`broken-ref` 는 error, 나머지는 warning. lint 통과 = front matter 와 prose 의 토큰 참조가 모두 resolve 됨.

## SSE Consumption (후속 PR)
## SSE Consumption

`agent` 의 `POST /api/generate` 가 6 단계 검증 progress 를 SSE 로 흘림. 1차 핸드오프에는 미포함.
`agent` 의 `POST /api/generate` 가 6 단계 검증 progress 를 SSE 로 흘림. 현재 구현됨 (`hooks/use-verification-stream.ts`).

**중요**: 브라우저 `EventSource` 는 GET 전용. `POST /api/generate` 와 호환되지 않음. 두 가지 선택지:
- (A) **`@microsoft/fetch-event-source`** 사용 — production 패턴 (OpenDerisk, Openinary, Kodus AI 채택). custom hook `hooks/use-verification-stream.ts` 안에 `fetchEventSource` 래핑 + `AbortController` cleanup.
- (B) `agent` 측에서 `GET /api/generate?…` 로 변경 — 표준 `EventSource` 사용 가능.

후속 PR 에서 (A) 채택 잠정. `useChat` (Vercel AI SDK) 은 AI SDK 의 `data-*` 메시지 스트림 프로토콜 전용이라 임의 SSE 이벤트 이름 (`step_started`, `pipeline_completed`) 과 호환 안 됨.
현재 구현은 (A) `@microsoft/fetch-event-source` 채택. `useChat` (Vercel AI SDK) 은 AI SDK 의 `data-*` 메시지 스트림 프로토콜 전용이라 임의 SSE 이벤트 이름 (`step_started`, `pipeline_completed`) 과 호환 안 됨.

이벤트 종류 (`docs/specs/architecture.md` D-6):
- `step` — `{ index: 1-6, name: string, status: 'started' | 'completed' | 'failed' }`
- `preview` — `{ latex: string }` (중간 후보 LaTeX 미리보기)
- `result` — `GeneratedProblem[]` (검증 통과한 문항 묶음)
- `error` — `{ stage: string, message: string }`

후속 PR scope:
- `lib/sse-client.ts` + `hooks/use-verification-stream.ts` (`fetchEventSource` + `AbortController` cleanup)
구현 내역:
- `hooks/use-verification-stream.ts` (`fetchEventSource` + `AbortController` cleanup) — 구현 완료
- React 18 strict-mode 더블 마운트 방어 (cleanup 함수)
- `agent` 측 CORS 설정 (`Access-Control-Allow-Origin: http://localhost:3001`)
- `agent` 측 CORS 설정 (`Access-Control-Allow-Origin: http://localhost:27182`)
- `@openmath/agent` 워크스페이스 dep 추가 + Zod 스키마 import (`@openmath/agent/schemas`)

## 폰트 로딩
Expand Down
6 changes: 3 additions & 3 deletions packages/web/hooks/use-verification-stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { useEffect, useReducer, useRef } from "react";
* 로컬 타입을 사용한다. 추후 zod 스키마 import 로 검증을 강화한다.
* ──────────────────────────────────────────────────────────── */

export const STEP_NAMES: readonly string[] = [
const STEP_NAMES: readonly string[] = [
"RAG 검색",
"의도 추출",
"문제 생성",
Expand Down Expand Up @@ -68,7 +68,7 @@ export type StreamInput = {
mode: "structural" | "conceptual";
dims: readonly string[];
sourceProblemText?: string;
/** override agent endpoint. defaults to NEXT_PUBLIC_AGENT_URL or localhost:3000 */
/** override agent endpoint. defaults to NEXT_PUBLIC_AGENT_URL or localhost:31415 */
endpoint?: string;
};

Expand Down Expand Up @@ -241,7 +241,7 @@ class FatalStreamError extends Error {}
function defaultEndpoint(): string {
const env =
typeof process !== "undefined" ? process.env.NEXT_PUBLIC_AGENT_URL : undefined;
return env ?? "http://localhost:3000";
return env ?? "http://localhost:31415";
}

function verificationStorageKey(input: StreamInput): string {
Expand Down
4 changes: 2 additions & 2 deletions packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
"private": true,
"type": "module",
"scripts": {
"dev": "next dev --port 3001",
"dev": "next dev --port 27182",
"build": "next build",
"start": "next start --port 3001",
"start": "next start --port 27182",
"lint": "next lint",
"typecheck": "tsc --noEmit"
},
Expand Down
Loading