# langchain-canvas **LangChain 에이전트를 위한 라이브 캔버스.** 에이전트는 평범한 툴만 작성하면 됩니다. 사용자는 캔버스를 얻습니다 — 채팅 옆에서 문서·차트·표·완전한 HTML 페이지가 실시간으로 렌더되고, 쓰이는 동안 스트리밍되며, 스스로 버전을 남기고, 어떤 요소든 클릭해서 편집할 수 있는 패널. 품질 기준: Genspark · ChatGPT Canvas · Claude Artifacts.
📖 [English](README.md) · **한국어**
``` ┌───────────────────────────┬─────────────────────────────────────┐ │ 채팅 │ 캔버스 │ │ │ ┌────────────────────────────────┐ │ │ › 가격 페이지 만들어줘 │ │ Starter Pro Enterprise │ │ │ │ │ $0 $20 문의하기 │ │ │ ✓ 페이지 완성 — 요소를 │ │ [ hover → 하이라이트, │ │ │ 클릭해 편집하세요. │ │ click → 이 요소만 편집 ] │ │ │ │ └────────────────────────────────┘ │ └───────────────────────────┴─────────────────────────────────────┘ ``` --- ## 목차 - [백엔드 없이 보기 (스키마 리플레이)](#백엔드-없이-보기-스키마-리플레이) - [내 앱에 캔버스 붙이기](#내-앱에-캔버스-붙이기) - [세 가지 핵심 아이디어](#세-가지-핵심-아이디어) - [기능](#기능) - [새 아티팩트 타입 추가하기](#새-아티팩트-타입-추가하기) - [문서](#문서) · [로드맵](#로드맵) · [라이선스](#라이선스) --- ## 백엔드 없이 보기 (스키마 리플레이) 캔버스는 전적으로 **와이어 스키마**(= `StreamEvent` 스트림)로 정의됩니다. 그래서 백엔드도, LLM도, API 키도 없이 **픽스처만으로** 렌더링할 수 있습니다. 가장 빠르게 확인하고 렌더러를 개발하는 방법입니다: ```bash pnpm install pnpm dev:web # → http://localhost:3000/replay 열기 ``` 시나리오(HTML 페이지 / 스트리밍 문서 / 차트 / 표)를 골라, 실제 에이전트가 구동하는 것과 똑같이 렌더되는 걸 보세요. 코드로는: ```tsx import { Canvas, useCanvasReplay, scenarios } from "@braincrew-lab/langchain-canvas"; const { play } = useCanvasReplay(); play(scenarios[0].events); // 스키마 → 화면, 네트워크 없음 ``` > LangChain/LangGraph 백엔드는 이 **동일한 이벤트**를 LangGraph의 `custom` 스트림 > 채널로 emit 합니다. 프론트엔드는 이벤트가 픽스처에서 왔는지 실제 에이전트에서 > 왔는지 신경 쓰지 않습니다. 지금은 픽스처로 개발하고, 백엔드가 준비되면 실제 > 에이전트를 꽂으면 됩니다. ## 내 앱에 캔버스 붙이기 설치 두 번, 코드 두 조각. > 아직 PyPI/npm 에 배포 전입니다 — 현재는 이 레포에서 직접 설치하세요 > (`apps/server/pyproject.toml`, `pnpm-workspace.yaml` 의 워크스페이스 설정 참고). ### 백엔드 (Python) — 툴에서 아티팩트 emit ```python from langchain.tools import tool, ToolRuntime from langchain_canvas import Canvas, create_canvas_agent, sse_from_agent @tool def build_page(brief: str, runtime: ToolRuntime) -> str: """HTML 페이지를 만들어 캔버스에 보여준다.""" canvas = Canvas.from_runtime(runtime) # 1. 캔버스 가져오기 page = canvas.open_html(title=brief) # 2. 아티팩트 열기 page.set_html("

Hello

") # 3. 채우기 (.append(...) 으로 스트리밍도 가능) page.complete() return "페이지를 캔버스에 띄웠습니다." agent = create_canvas_agent(model="anthropic:claude-sonnet-4-5", tools=[build_page]) ``` FastAPI 로 SSE 스트리밍: ```python from fastapi import FastAPI from fastapi.responses import StreamingResponse from pydantic import BaseModel app = FastAPI() class Body(BaseModel): thread_id: str message: str @app.post("/api/chat") async def chat(body: Body): inputs = {"messages": [{"role": "user", "content": body.message}]} config = {"configurable": {"thread_id": body.thread_id}} return StreamingResponse(sse_from_agent(agent, inputs, config=config), media_type="text/event-stream") ``` ### 프론트엔드 (React) — 렌더링 ```tsx "use client"; import { Canvas, useCanvasStream } from "@braincrew-lab/langchain-canvas"; import "@braincrew-lab/langchain-canvas/styles.css"; export default function Page() { const { sendMessage, messages, canvas, isStreaming, editSelection } = useCanvasStream({ endpoint: "/api/chat" }); return (
{/* 클릭-편집이 연결됨 */}
); } ``` 끝입니다. `useCanvasStream` 이 메시지를 보내고 스트림을 파싱해 대화(`messages`)와 캔버스(`canvas`)를 동기화하며, `` 는 에이전트가 emit 한 것을 그립니다. 채팅 말풍선만 직접 만들면, 캔버스는 완성되어 있습니다. 양쪽 전체 예제는 [`docs/03-getting-started.md`](docs/03-getting-started.md) 에 있습니다. --- ## 세 가지 핵심 아이디어 최신 캔버스 제품들(ChatGPT `canmore`, Claude `antArtifact`, Vercel AI SDK data parts)은 모두 같은 설계로 수렴합니다. `langchain-canvas` 는 그 설계를 최소한으로 담았습니다: 1. **아티팩트는 emit 하지, 파싱하지 않는다.** 에이전트는 툴을 호출해 캔버스를 엽니다 — 프롬프트 안에 매직 토큰 같은 건 없습니다. 2. **`type` 문자열이 렌더러를 고른다.** 백엔드는 데이터(`{ type, data }`)만 보내고 JSX 는 보내지 않습니다. 프론트엔드가 `type → 컴포넌트` 레지스트리를 소유합니다. 3. **안정적인 `id` 가 모든 걸 리컨실한다.** 같은 `id` → 제자리 갱신, 새 `id` → 새 아티팩트. 이 규칙 하나가 스트리밍·패치·버전관리를 전부 굴립니다. 내부적으로는 LangChain 1.x 의 네이티브 커스텀 스트림 채널 (`ToolRuntime.stream_writer` → `stream_mode="custom"`) 위에서 동작합니다 — 프레임워크 포크 없음. ## 기능 - 🌐 **HTML이 베이스** — 에이전트가 자기완결형 페이지를 emit 하고, CSP 샌드박스 iframe 에서 렌더됩니다. 문서·차트·표는 그 위의 구조화된 편의 타입입니다. - 🖱️ **클릭-편집** — hover 하이라이트, 클릭 선택 후 자연어 지시(에이전트가 그 요소만 수술적으로 패치)를 하거나, **스타일 패널**(색/크기/굵기/정렬)과 **더블클릭 인라인 텍스트 편집**을 씁니다. - ⚡ **O(1) 요소 패치** — `canvas.node_patch` 가 페이지를 통째로 다시 보내지 않고 `data-cid` 로 한 요소만 교체합니다. - 📝 **스트리밍 문서** — 마크다운이 토큰 단위로 실시간 렌더. - 📊 **차트** & 📋 **표** — line/bar/area/pie, 정돈된 rows 위의 sticky 헤더 그리드. - 📦 **파일 export** — 어떤 아티팩트든 자기완결형 **`.html`**, 그리고 `.md` / `.csv` / `.json`. - 🗂️ **탭 + 버전관리** — 아티팩트 전환, 모든 버전 넘겨보기. - 🧩 **교체 가능한 렌더러** & 🔌 **헤드리스 코어** — `type → 컴포넌트` 등록, 또는 리컨실러/SSE 클라이언트만 써서 직접 UI 구성. - 🧵 **양쪽 타입 안전** — Pydantic 과 TypeScript 가 하나의 와이어 프로토콜을 미러링. ## 새 아티팩트 타입 추가하기 세 단계, 전송 계층 변경 0: 1. 데이터 shape 를 양쪽 `protocol` 모듈(Python + TS)에 추가. 2. 툴에서 emit (`canvas.open_*`, 또는 raw `canvas.create`). 3. 렌더러 등록: ``. ## 문서 - [아키텍처](docs/01-architecture.md) — 경계와 그 이유. - [와이어 프로토콜](docs/02-protocol.md) — 모든 이벤트와 리컨실 효과. - [시작하기](docs/03-getting-started.md) — 앞뒤 전체 복붙 예제. - [기여 가이드](CONTRIBUTING.md). ## 로드맵 - 원클릭 **publish → 공유 URL** 및 `