Skip to main content
Glama

문서 변환 API

PDF/Office 문서와 구조화된 JSON 간의 양방향 변환. NEXUS 후보 #6 -- 수동 빌드, FORGE 생성 아님, 후보 #3(agent-verification-api) 및 후보 #4(url-metadata-api)와 동일한 수동 Cloud Run 자산 패턴을 따름.

  • POST /extract-pdf-to-json -- 텍스트, 표(페이지별), 페이지 수, 메타데이터. $0.02/호출.

  • POST /extract-docx-to-json -- 문단(제목 수준/스타일 포함), 표, 메타데이터. $0.01/호출.

  • POST /extract-xlsx-to-json -- 시트별 셀 그리드, 행/열 상한 적용. $0.01/호출.

  • POST /generate-pdf-from-json -- 구조화된 블록(제목/문단/표) -> PDF 바이트. $0.02/호출.

  • POST /generate-docx-from-json -- 구조화된 블록 -> .docx 바이트. $0.01/호출.

  • /mcp의 MCP 도구가 5개 모두 미러링 -- 현재 무료, "알려진 제한 사항" 참조.

  • GET /health, GET /.well-known/agent-card.json, GET /openapi.json (x-payment-info 포함).

5개 엔드포인트 모두 base64로 인코딩된 파일 바이트를 요청 본문에 직접 받음 -- 가져올 URL이 아님. 이는 의도적인 범위 경계입니다. 이 자산의 위험 표면에서 SSRF를 완전히 제거합니다(url-metadata-api/agent-verification-api와 달리, 둘 다 호출자가 제공한 URL을 가져오고 SSRF 가드가 필요함).

왜 이 라이브러리들인가, 왜 외부 데이터 소스가 없는가

순수 OSS, 성숙함, LLM 호출 없음, 런타임 시 외부 네트워크 호출 0회(완전히 로컬 계산): pdfplumber(PDF 추출, pdfminer.six 래핑), python-docx(Word), openpyxl(Excel), reportlab(PDF 생성). 이 자산 어디에도 명명된 제3자 데이터 소스가 없음 -- 의도적으로, BuyWhere 스타일의 환각 위험(skills/asset-lifecycle)을 피하기 위해: 외부에서 잘못될 것이 없고, 제품 전체가 "호출자가 보낸 바이트에 잘 알려진 라이브러리를 실행하는 것"입니다. numpy/scipy 없음 (알려진 Cloud Run Buildpacks 실패 회피 -- scipy의 소스 빌드를 위한 Fortran 컴파일러 없음, skills/infra-deploy-ops 참조; 이 라이브러리들 중 어느 것도 필요로 하지 않음).

Related MCP server: docxtpl MCP Server

왜 $0.01-$0.02인가 (agent-verification-api의 $0.35, url-metadata-api의 $0.01과 비교)

유료 제3자 API 비용이 없음(live-entity-verification/agent-verification-api의 WHOIS와 달리) -- 순수 CPU/메모리 비용이므로, url-metadata-api의 $0.01 단일 가져오기와 같은 낮은 계층에 속하며 agent-verification-api의 $0.35와는 거리가 멀습니다. PDF 작업(extract-pdf-to-json, generate-pdf-from-json)은 docx/xlsx($0.01)보다 한 단계 높은 가격($0.02)입니다: pdfplumber/reportlab은 호출당 훨씬 더 많은 작업(페이지 수준 레이아웃 파싱 / PDF 렌더링)을 수행하는 반면, python-docx/openpyxl은 zip 아카이브의 단순 XML 파싱입니다. 이 자산은 3개의 수동 후보 중 가장 예측 가능한 자산이 될 것으로 예상되며 최고 수익자는 아닙니다 -- 문서 변환은 일반적이고 변동성이 낮은 에이전트 요구이지, 차별화된/희귀한 기능이 아니므로 -- 가격은 호출당 마진보다 일관된 저마찰 사용을 선호합니다.

이 자산이 다른 3개의 수동 자산에는 없는 두 가지 위험

  1. CPU 바운드, I/O 바운드 아님. 모든 파서/생성기 호출은 동기식입니다(4개 라이브러리 모두 비동기 API 없음), 다른 3개 자산이 httpx.AsyncClient를 통해 I/O 바운드인 것과 다릅니다. 모든 핸들러는 asyncio.wait_for() 타임아웃(25초) 내에서 asyncio.to_thread()를 통해 실제 작업을 오프로드하므로, 한 호출자의 느린 파싱이 전체 이벤트 루프/모든 동시 요청을 차단하지 않습니다. 알려진 잔여 제한 사항: asyncio.wait_for()는 대기 중인 작업을 취소하지만, 기본 OS 스레드를 종료할 수는 없습니다 -- Python에는 실행 중인 스레드를 강제로 종료할 API가 없습니다. pdfminer/openpyxl을 내부적으로 중단시키는 병리학적 입력은 호출자가 504를 받은 후에도 해당 작업자 스레드 하나를 무기한 점유합니다. 완화되었지만 해결되지는 않음: 바운드 세마포어 (NEXUS_MAX_CONCURRENT_JOBS, 기본 4)가 동시에 누적될 수 있는 "누출된" 스레드 수를 제한합니다 -- 새 요청은 무제한 스레드 증가 대신 깨끗한 503을 받지만, 이미 누출된 스레드는 회수되지 않습니다. 완전한 수정은 CPU 바운드 작업을 스레드가 아닌 별도의 종료 가능한 프로세스(ProcessPoolExecutor + 하드 종료)에서 수행해야 합니다. 7일 평가판 후보에게는 과도한 것으로 간주되어 여기서는 수행하지 않습니다.

  2. 압축 해제 폭탄 / 리소스 고갈. .docx/.xlsx는 zip 아카이브입니다 -- 작은 압축 크기지만 큰 압축 해제 크기를 가진 조작된 파일(zip 폭탄)은 다른 3개 자산이 직면한 것과 구별되는 실제 DoS 벡터입니다. _check_zip_bomb_safe()는 바이트를 python-docx/openpyxl에 넘기기 전에 zip 중앙 디렉터리(zipfile.infolist(), 저렴함, 항목 데이터를 압축 해제하지 않음)를 검사합니다: 총 압축 해제 크기가 50MB를 초과하거나 단일 항목의 압축 비율이 100배를 초과하면 거부합니다. 모든 업로드는 파싱 전에 원시/디코딩 바이트 8MB로 제한됩니다(PDF 포함, zip 기반이 아니더라도 -- 형식에 관계없이 최악의 입력 크기를 제한).

알려진 제한 사항 (의도적으로 수정하지 않음 -- CLAUDE.md §3, 필요하다는 증거 없이는 게이트 없음)

  • MCP 도구 호출은 청구되지 않음. 형제 수동 자산과 동일한 인프로세스 호출 패턴(및 동일한 이유): MCP 도구는 HTTP 재진입을 통한 ASGI 앱 호출이 아니라 공유 변환 함수를 직접 호출합니다.

  • 호출자별 속도 제한 없음. 7일 일회용 측정에는 적합; 유지된다면 추가.

  • 생성된 문서는 로컬에서 스모크 테스트된 것 이상의 왕복 충실도로 검증되지 않음 -- reportlab의 표/문단 렌더링과 python-docx의 제목 수준 매핑은 모두 성숙하고 널리 사용되는 코드 경로이며, 여기서 모든 가능한 Office/PDF 리더에 대해 재검증되지는 않습니다.

  • 스레드 누출 잔여 제한 사항은 위의 "CPU 바운드" 위험 참조.

NEXUS_X402_FREE_MODE

similarity-search-api/live-entity-verification(skills/x402-payments)과 동일한 게이트 패턴 -- 기본값 false(첫날부터 청구, 무료 체험 기간 없음; 엔진은 live-entity-verification의 WHOIS 기반 엔진이 가졌던 외부 검증 의존성이 없으므로, 유료로 바로 전환할 만한 "이미 프로덕션에서 입증됨"이라는 동등한 논거가 없음 -- 어쨌든 세션 브리프의 "적당하고 안정적인" 수익 프레임워크에 따라 청구됨, 특정 선례 때문이 아님). 실제 facilitator 왕복 없이 테스트하려면 로컬에서 true로 설정.

배포 대상: Cloud Run, Railway 아님

후보 #3/#4와 동일한 파이프라인 -- skills/infra-deploy-ops 참조. 메모리를 1Gi로 상향 조정 (공유 scripts/deploy_cloud_run.sh의 하드코딩된 512Mi 기본값 대비) -- 3개의 수동 후보 중 메모리 사용량이 가장 큼 (PDF/Office 파싱 라이브러리, pypdfium2/Pillow가 pdfplumber를 통해 전이적으로 포함). 공유 스크립트를 통하지 않고 gcloud run deploy로 직접 배포(단일 후보의 메모리 상향을 위해 공유 인프라 도구를 수정하지 않기 위해):

# 1. First deploy -- PUBLIC_DOMAIN not known yet, every real request 421s until step 2.
gcloud run deploy document-conversion-api \
  --source manual_assets/document-conversion-api \
  --project nexus-505016 --region us-central1 \
  --allow-unauthenticated --min-instances=0 --max-instances=3 --memory=1Gi --quiet \
  --env-vars-file manual_assets/document-conversion-api/env-vars.deploy.yaml

# 2. Grab the printed *.run.app URL, then:
gcloud run services update document-conversion-api --region us-central1 --project nexus-505016 \
    --update-env-vars PUBLIC_DOMAIN=<the-real-domain>

측정 (후보 #6, 7일 창)

첫 실제 배포 후 7일 창. 진실의 원천: traffic_events/revenue_events/mcp_call_events 테이블(asset_name = 'document-conversion-api'), Cloud Run 로그가 아님. 7일째: 실제 트래픽이 0이면(크롤러 필터링), Cloud Run 서비스를 일시 중지/삭제, 후보 #3/#4와 동일한 결정 규칙.

Related MCP Connectors

Related MCP Servers