feat: 저작권 탭 경량 응답 API 추가
This commit is contained in:
parent
91e356e4c1
commit
6dd5fa4a2e
@ -13,6 +13,11 @@ from app.api.schemas import (
|
||||
CorpusListResponse,
|
||||
CorpusUploadRequest,
|
||||
CorpusUploadResponse,
|
||||
CopyrightAiItem,
|
||||
CopyrightCountItem,
|
||||
CopyrightLegalJudgment,
|
||||
CopyrightReviewResponse,
|
||||
CopyrightScoreCard,
|
||||
DetectRequest,
|
||||
DetectResponse,
|
||||
HealthResponse,
|
||||
@ -92,6 +97,76 @@ async def detect(req: DetectRequest, request: Request) -> DetectResponse:
|
||||
return await run_in_threadpool(_detector(request).detect_request, req)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/plagiarism/review",
|
||||
response_model=CopyrightReviewResponse,
|
||||
tags=["plagiarism"],
|
||||
summary="저작권 탭 경량 검사",
|
||||
)
|
||||
async def copyright_review(
|
||||
req: DetectRequest, request: Request,
|
||||
) -> CopyrightReviewResponse:
|
||||
"""상세 탐지는 수행하되 저작권 탭에 필요한 필드만 작게 반환한다."""
|
||||
result = await run_in_threadpool(_detector(request).detect_request, req)
|
||||
review = result.review_summary
|
||||
legal = result.legal_risk
|
||||
if review is None or legal is None:
|
||||
raise HTTPException(status_code=503, detail="Copyright review is unavailable")
|
||||
|
||||
ai_labels = {
|
||||
"low": "낮음",
|
||||
"medium": "중간",
|
||||
"high": "높음",
|
||||
"unknown": "확인 불가",
|
||||
}
|
||||
if legal.llm_verdict == "likely":
|
||||
legal_status, legal_label = "suspected", "판례에 비추어 저작권 침해 의심"
|
||||
elif (
|
||||
legal.llm_verdict == "unlikely"
|
||||
or legal.status == "no_registered_corpus_match"
|
||||
):
|
||||
legal_status, legal_label = "low", "등록 판례 기준 침해 의심 낮음"
|
||||
elif legal.status == "insufficient_precedent_data":
|
||||
legal_status, legal_label = "unavailable", "판례 검토 불가"
|
||||
else:
|
||||
legal_status, legal_label = "review_required", "관련 판례에 따른 추가 검토 필요"
|
||||
|
||||
precedent_ids = list(legal.llm_matched_precedent_ids or legal.precedent_ids)
|
||||
suspicion_text = (
|
||||
"표절 의심 구간이 확인되었습니다."
|
||||
if review.has_suspicion
|
||||
else "표절 의심 구간이 없습니다."
|
||||
)
|
||||
return CopyrightReviewResponse(
|
||||
doc_id=result.doc_id,
|
||||
copyright=CopyrightScoreCard(
|
||||
originality_percent=review.originality_percent,
|
||||
similarity_percent=review.similarity_percent,
|
||||
compared_count=review.compared_count,
|
||||
has_suspicion=review.has_suspicion,
|
||||
description=(
|
||||
f"등록 원문 검색 세그먼트 {review.compared_count:,}건과 대조한 결과 "
|
||||
f"{suspicion_text}"
|
||||
),
|
||||
),
|
||||
similar_sentences=CopyrightCountItem(
|
||||
count=review.similar_sentence_count,
|
||||
label=f"{review.similar_sentence_count}건",
|
||||
),
|
||||
ai_generation_suspicion=CopyrightAiItem(
|
||||
level=review.ai_suspicion_level,
|
||||
label=ai_labels[review.ai_suspicion_level],
|
||||
),
|
||||
legal_judgment=CopyrightLegalJudgment(
|
||||
status=legal_status,
|
||||
label=legal_label,
|
||||
summary=legal.judgment_summary,
|
||||
precedent_ids=precedent_ids,
|
||||
),
|
||||
analyzed_at=result.analyzed_at,
|
||||
)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/summary",
|
||||
response_model=SummaryResponse,
|
||||
|
||||
@ -288,6 +288,44 @@ class DetectResponse(BaseModel):
|
||||
analyzed_at: datetime
|
||||
|
||||
|
||||
class CopyrightScoreCard(BaseModel):
|
||||
"""저작권 탭 상단 카드에 그대로 표시할 값."""
|
||||
|
||||
originality_percent: int = Field(..., ge=0, le=100)
|
||||
similarity_percent: int = Field(..., ge=0, le=100)
|
||||
compared_count: int = Field(..., ge=0)
|
||||
has_suspicion: bool
|
||||
description: str
|
||||
|
||||
|
||||
class CopyrightCountItem(BaseModel):
|
||||
count: int = Field(..., ge=0)
|
||||
label: str
|
||||
|
||||
|
||||
class CopyrightAiItem(BaseModel):
|
||||
level: Literal["low", "medium", "high", "unknown"]
|
||||
label: str
|
||||
|
||||
|
||||
class CopyrightLegalJudgment(BaseModel):
|
||||
status: Literal["suspected", "low", "review_required", "unavailable"]
|
||||
label: str
|
||||
summary: str
|
||||
precedent_ids: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class CopyrightReviewResponse(BaseModel):
|
||||
"""나누구 저작권 탭 전용 경량 응답."""
|
||||
|
||||
doc_id: str
|
||||
copyright: CopyrightScoreCard
|
||||
similar_sentences: CopyrightCountItem
|
||||
ai_generation_suspicion: CopyrightAiItem
|
||||
legal_judgment: CopyrightLegalJudgment
|
||||
analyzed_at: datetime
|
||||
|
||||
|
||||
class BatchItem(BaseModel):
|
||||
doc_id: str
|
||||
text: str
|
||||
|
||||
@ -1,92 +1,59 @@
|
||||
# 저작권 탐지 API 상세 연동 가이드 (오투오 → 바이칼)
|
||||
# 저작권 탭 API 연동 가이드
|
||||
|
||||
> 나누구 앱 연동용. 각 엔드포인트 사용법 · 응답 변수 의미 · **저작권 탭 화면을 그리기
|
||||
> 위한 호출 순서**를 정리한다. 실시간 스키마: `https://plagiarism.o2o.kr/docs`
|
||||
> 엔진: `o2o-plagiarism-2.1.0-kosimcse`
|
||||
> 최종 갱신: `2026-08-18` · 판례 545건 및 GPT 판례 판단 연동 반영
|
||||
> 나누구 모바일 앱의 저작권 탭 전용 경량 API 문서
|
||||
>
|
||||
> 최종 갱신: 2026-08-18
|
||||
|
||||
---
|
||||
## 1. 연동 요약
|
||||
|
||||
## 0. 공통
|
||||
저작권 탭은 아래 API를 한 번 호출해 렌더링한다.
|
||||
|
||||
```text
|
||||
POST https://plagiarism.o2o.kr/v1/plagiarism/review
|
||||
```
|
||||
|
||||
이 API는 내부적으로 표절 탐지, AI 생성 의심도 산출, 판례 비교를 수행하지만 앱에는
|
||||
화면에 필요한 요약값만 반환한다. 상세 탐지 API의 `matches`, `evidence_spans`,
|
||||
`extracted_elements`, `score_semantics`, 모델 정보 등은 응답하지 않는다.
|
||||
|
||||
## 2. 공통 규격
|
||||
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| Base URL | `https://plagiarism.o2o.kr` |
|
||||
| 공통 prefix | 모든 API는 `/v1` 으로 시작 |
|
||||
| 요청/응답 | JSON, UTF-8, `Content-Type: application/json` |
|
||||
| 인증 | 개발 기본값은 무인증. 운영에서 설정된 경우 `X-API-Key` 헤더 필수 (`/v1/health` 공개 여부는 서버 설정) |
|
||||
| 시간 형식 | ISO 8601 UTC (예: `2026-08-04T04:37:00Z`) |
|
||||
| Method | `POST` |
|
||||
| Path | `/v1/plagiarism/review` |
|
||||
| Content-Type | `application/json; charset=utf-8` |
|
||||
| 인증 | 운영에서 설정된 경우 `X-API-Key` 헤더 사용 |
|
||||
| 시간 | ISO 8601 UTC |
|
||||
|
||||
### 엔드포인트 한눈에
|
||||
## 3. 요청
|
||||
|
||||
| # | Method | Path | 용도 | 화면 연동 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | GET | `/v1/health` | 엔진 상태·코퍼스 크기 확인 | 앱 기동 시 1회 |
|
||||
| 2 | POST | `/v1/plagiarism/detect` | **본문 1건 표절 탐지** | **저작권 탭 핵심** |
|
||||
| 3 | POST | `/v1/plagiarism/batch` | 배치 탐지(≤500) 등록 | 대량 검사용 |
|
||||
| 4 | GET | `/v1/plagiarism/batch/{job_id}` | 배치 결과 조회 | 대량 검사용 |
|
||||
| 5 | POST | `/v1/summary` | 스토리 요약 | 요약 기능용 |
|
||||
| 6 | GET | `/v1/taxonomy` | 법령 태그·케이스 정의 | 라벨 캐시용 |
|
||||
| 7 | GET | `/v1/corpus` | 코퍼스 목록 | 운영/관리 |
|
||||
| 8 | POST | `/v1/corpus` | 코퍼스 등록(JSON) | 운영/관리 |
|
||||
| 9 | POST | `/v1/corpus/file` | 코퍼스 등록(.txt 업로드) | 운영/관리 |
|
||||
| 10 | DELETE | `/v1/corpus/{doc_id}` | 코퍼스 삭제 | 운영/관리 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 저작권 탭을 그리기 위한 호출 순서 ★
|
||||
|
||||
가장 중요한 부분. 저작권 탭 화면 하나를 채우는 데 필요한 호출 흐름이다.
|
||||
|
||||
```
|
||||
[앱 최초 기동 / 세션 시작 시 — 1회]
|
||||
① GET /v1/health → 엔진 정상·corpus_size 확인 (실패면 탭 비활성)
|
||||
② GET /v1/taxonomy → 태그/케이스 한글 라벨 캐시 (표절 상세 표시용, 선택)
|
||||
|
||||
[사용자가 에피소드 작성 후 '저작권' 탭을 열 때 — 매 검사]
|
||||
③ POST /v1/plagiarism/detect { doc_id, text: 에피소드 본문 }
|
||||
└ 응답 1건으로 저작권 탭 전체를 렌더링:
|
||||
• review_summary → 상단 요약(독창성/유사도/유사문장/AI 의심도)
|
||||
• matches[] → 표절 의심 시 상세 목록
|
||||
• matches[].evidence_spans → 본문 내 일치 구간 하이라이트
|
||||
• matches[].tags / case_id → 침해 유형 배지 (②의 라벨과 결합)
|
||||
• ccl_basis → 사람이 읽는 판정 근거 문장
|
||||
• legal_risk → 판례 Top 5 + GPT/규칙 기반 법적 검토 보조
|
||||
• score_semantics → 점수·임계값의 의미와 잠정 여부
|
||||
```
|
||||
|
||||
**핵심: 탭 렌더링에 필요한 호출은 `detect` 단 1번**이다. `health`/`taxonomy`는
|
||||
세션당 1회 캐시하면 된다. 화면 상단 숫자는 전부 `review_summary`에서 나오므로,
|
||||
바이칼 쪽에서 별도 계산할 것은 없다.
|
||||
|
||||
### 화면 요소 ↔ 응답 필드 매핑
|
||||
|
||||
| 저작권 탭 화면 | 값 예시 | 응답 경로 |
|
||||
|---|---|---|
|
||||
| 독창성 98% | 98 | `review_summary.originality_percent` |
|
||||
| 저작권 · 유사도 2% | 2 | `review_summary.similarity_percent` |
|
||||
| "…3.5만 건과 대조한 결과 표절 의심 구간이 없습니다" | 35000 / false | `review_summary.compared_count`, `review_summary.has_suspicion` |
|
||||
| 유사 문장 0건 | 0 | `review_summary.similar_sentence_count` |
|
||||
| AI 생성 의심도 | unknown | `review_summary.ai_suspicion_level` (`low/medium/high/unknown`) |
|
||||
| (표절 시) 일치 구간 하이라이트 | start~end | `matches[].evidence_spans[]` |
|
||||
| (표절 시) 침해 유형 배지 | 복제권 | `matches[].tags[].label_ko` + `case_id` |
|
||||
| 판례 기반 검토 의견 | 판단 필요 | `legal_risk.llm_verdict`, `legal_risk.supporting_reasons[]` |
|
||||
| 관련 판례 | 사건번호 | `legal_risk.llm_matched_precedent_ids[]` 또는 `precedent_ids[]` |
|
||||
|
||||
---
|
||||
|
||||
## 2. `POST /v1/plagiarism/detect` — 표절 탐지 (핵심)
|
||||
|
||||
본문 1건을 검사해 저작권 탭에 필요한 모든 데이터를 반환한다.
|
||||
|
||||
### 요청 본문
|
||||
### 최소 요청
|
||||
|
||||
```json
|
||||
{
|
||||
"doc_id": "episode-001",
|
||||
"text": "창밖으로 보이는 숲은 오늘따라 유난히 푸르게 보였다. ...",
|
||||
"metadata": { "title": "에피소드 1", "author": "홍길동", "genre": "자서전" },
|
||||
"options": { "return_evidence": true, "threshold": null, "top_k": 5, "autobiography_mode": null },
|
||||
"text": "창밖으로 보이는 숲은 오늘따라 유난히 푸르게 보였다."
|
||||
}
|
||||
```
|
||||
|
||||
### 전체 요청
|
||||
|
||||
```json
|
||||
{
|
||||
"doc_id": "episode-001",
|
||||
"text": "검사할 에피소드 본문 전체",
|
||||
"metadata": {
|
||||
"title": "에피소드 1",
|
||||
"author": "홍길동",
|
||||
"genre": "자서전"
|
||||
},
|
||||
"options": {
|
||||
"threshold": null,
|
||||
"top_k": 5,
|
||||
"autobiography_mode": null
|
||||
},
|
||||
"legal_context": {
|
||||
"work_type": "literary",
|
||||
"access_evidence": null,
|
||||
@ -96,299 +63,166 @@
|
||||
}
|
||||
```
|
||||
|
||||
| 필드 | 타입 | 필수 | 의미 |
|
||||
|---|---|---|---|
|
||||
| `doc_id` | string | ✅ | 호출측 문서 식별자. 응답에 그대로 반환(요청-응답 짝 맞춤용) |
|
||||
| `text` | string | ✅ | 검사할 본문 (1자 이상) |
|
||||
| `metadata.title` | string | – | 작품 제목 (선택) |
|
||||
| `metadata.author` | string | – | 저자 (선택) |
|
||||
| `metadata.genre` | string | – | 장르 (선택) |
|
||||
| `metadata.publisher` | string | – | 출판사 (선택) |
|
||||
| `metadata.publication_year` | int | – | 출판연도 (선택) |
|
||||
| `options.return_evidence` | bool | – | 일치 구간(`evidence_spans`) 반환 여부. 기본 `true` |
|
||||
| `options.threshold` | float 0~1 \| null | – | 표절 판정 임계값. `null`이면 서버 기본(0.85) |
|
||||
| `options.top_k` | int | – | 반환할 최대 매칭 수. 기본 5 |
|
||||
| `options.autobiography_mode` | bool \| null | – | 자서전 특화 전처리. `null`이면 서버 설정 |
|
||||
| `legal_context.work_type` | string | – | 판례 검색용 저작물 유형. 기본 `literary` |
|
||||
| `legal_context.access_evidence` | bool \| null | – | 원저작물 접근·의거 가능성 확인 여부. 미확인은 `null` |
|
||||
| `legal_context.protected_expression_reviewed` | bool | – | 보호되는 창작적 표현인지 사람이 검토했는지 |
|
||||
| `legal_context.rights_verified` | bool | – | 권리 귀속·이용허락·인용 요건 확인 여부 |
|
||||
| 필드 | 타입 | 필수 | 설명 |
|
||||
|---|---|---:|---|
|
||||
| `doc_id` | string | O | 호출 측 문서 또는 에피소드 ID |
|
||||
| `text` | string | O | 검사할 본문, 1자 이상 |
|
||||
| `metadata` | object | X | 제목, 저자, 장르 등 표시·추적용 메타데이터 |
|
||||
| `options.threshold` | number/null | X | 탐지 임계값. 미지정 시 서버 기본값 |
|
||||
| `options.top_k` | integer | X | 내부 비교 후보 수, 기본 5 |
|
||||
| `options.autobiography_mode` | boolean/null | X | 자서전 특화 전처리 사용 여부 |
|
||||
| `legal_context` | object | X | 사람이 확인한 접근 가능성·권리관계 등의 법적 맥락 |
|
||||
|
||||
> `legal_context`는 검토자가 확인한 사실만 전달한다. 서버와 GPT는 원고만 보고
|
||||
> 접근 가능성, 권리 귀속 또는 이용허락 여부를 임의로 추론하지 않는다.
|
||||
일반적인 저작권 탭 연동에서는 `doc_id`, `text`만 전달하면 된다.
|
||||
|
||||
### 응답 — 전체 필드 의미
|
||||
## 4. 응답
|
||||
|
||||
### 침해 의심이 낮은 예시
|
||||
|
||||
```json
|
||||
{
|
||||
"doc_id": "episode-001",
|
||||
"is_infringement": false,
|
||||
"confidence": 0.14,
|
||||
"review_summary": {
|
||||
"copyright": {
|
||||
"originality_percent": 98,
|
||||
"similarity_percent": 2,
|
||||
"similar_sentence_count": 0,
|
||||
"compared_count": 35000,
|
||||
"compared_count": 31560,
|
||||
"has_suspicion": false,
|
||||
"ai_suspicion_level": "low"
|
||||
"description": "등록 원문 검색 세그먼트 31,560건과 대조한 결과 표절 의심 구간이 없습니다."
|
||||
},
|
||||
"ai_generation": { "suspicion_level": "unknown", "score": null, "available": false, "is_stub": false, "model_version": "unavailable", "note": "학습된 모델이 없어 채점하지 않음" },
|
||||
"matches": [],
|
||||
"extracted_elements": { "characters": [], "motifs": ["비밀의 정원"], "genre": null, "keywords": ["숲","전설"] },
|
||||
"ccl_basis": null,
|
||||
"has_similarity_match": false,
|
||||
"corpus_scope_note": "현재 등록된 원천 문서와 검색 세그먼트만 대조했습니다. 미매칭은 비침해 확정이 아닙니다.",
|
||||
"legal_risk": {
|
||||
"status": "no_registered_corpus_match",
|
||||
"risk_level": "low",
|
||||
"similarity_evidence": "검색 유사도 0.000, 질의 커버리지 0.000, 최장 연속 일치 0자",
|
||||
"protected_expression": "not_reviewed",
|
||||
"access_evidence": "not_provided",
|
||||
"missing_factors": ["보호되는 창작적 표현인지에 대한 사람 검토", "원저작물 접근·의거 가능성", "저작권 귀속·이용허락·인용 요건"],
|
||||
"precedent_ids": [],
|
||||
"judgment_method": "rule_based",
|
||||
"llm_verdict": null,
|
||||
"llm_confidence": null,
|
||||
"llm_review_required": null,
|
||||
"llm_matched_precedent_ids": [],
|
||||
"supporting_reasons": [],
|
||||
"counter_reasons": [],
|
||||
"judge_model": null,
|
||||
"judge_prompt_version": null,
|
||||
"judge_note": null,
|
||||
"judgment_summary": "등록 코퍼스에서 일치 증거가 확인되지 않아 판례 기반 침해 의심을 제시하지 않습니다. 다만 미매칭은 비침해 확정이 아닙니다.",
|
||||
"disclaimer": "이 결과는 등록 코퍼스와 판례에 기반한 검토 우선순위이며 법률상 침해 확정이 아닙니다."
|
||||
"similar_sentences": {
|
||||
"count": 0,
|
||||
"label": "0건"
|
||||
},
|
||||
"score_semantics": {
|
||||
"combined_score": 0.14,
|
||||
"score_kind": "lexical_lemma_blend",
|
||||
"threshold_used": 0.85,
|
||||
"threshold_source": "server_default",
|
||||
"threshold_calibrated": false,
|
||||
"provisional": true,
|
||||
"union_coverage": 0.0,
|
||||
"covered_chars": 0,
|
||||
"query_chars": 42,
|
||||
"evidence_truncated": false
|
||||
"ai_generation_suspicion": {
|
||||
"level": "low",
|
||||
"label": "낮음"
|
||||
},
|
||||
"autobiography_mode": true,
|
||||
"candidates_before_filter": 3,
|
||||
"engine_version": "o2o-plagiarism-2.1.0-kosimcse",
|
||||
"analyzed_at": "2026-08-04T04:37:00Z"
|
||||
"legal_judgment": {
|
||||
"status": "low",
|
||||
"label": "등록 판례 기준 침해 의심 낮음",
|
||||
"summary": "등록 코퍼스에서 일치 증거가 확인되지 않아 판례 기반 침해 의심을 제시하지 않습니다. 다만 미매칭은 비침해 확정이 아닙니다.",
|
||||
"precedent_ids": []
|
||||
},
|
||||
"analyzed_at": "2026-08-18T03:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**최상위 필드**
|
||||
### 판례에 따른 침해 의심 예시
|
||||
|
||||
| 필드 | 타입 | 의미 | 화면 사용 |
|
||||
|---|---|---|---|
|
||||
| `doc_id` | string | 요청의 doc_id 그대로 | 응답 매칭 |
|
||||
| `is_infringement` | bool | 후방호환 필드. 법적 침해 확정이 아니라 임계 초과 매칭 존재 여부 | — |
|
||||
| `confidence` | float 0~1 | 최상위 매칭의 **결합 유사도 원값**. ⚠️ 임베딩 성분 때문에 무관한 글도 높게 나옴 → **화면 표시는 `review_summary` 사용, `confidence` 직접 표시 금지** | ✗ |
|
||||
| `review_summary` | object | **저작권 탭 UI 요약** (아래 상세) | ★ |
|
||||
| `ai_generation` | object | AI 생성 의심도 상세 (아래 상세) | 참고 |
|
||||
| `matches` | array | 매칭된 원본별 상세. 표절 아니면 `[]` | 표절 상세 |
|
||||
| `extracted_elements` | object | 본문에서 추출한 구성요소 `characters/motifs/genre/keywords` | 부가 |
|
||||
| `ccl_basis` | string\|null | 사람이 읽는 판정 근거 문장. 표절 아니면 `null` | 상세 문구 |
|
||||
| `has_similarity_match` | bool | 등록 코퍼스에서 채택된 매칭 존재 여부 | 분기 |
|
||||
| `corpus_scope_note` | string | 미매칭의 한계를 설명하는 안내 문구 | 안내 |
|
||||
| `legal_risk` | object | 등록 판례와 선택적 GPT Judge 기반의 **법적 검토 보조** | 검토 카드 |
|
||||
| `score_semantics` | object | 점수 종류·임계값·커버리지·잠정 여부 | 감사/상세 |
|
||||
| `autobiography_mode` | bool | 자서전 전처리 적용 여부 | — |
|
||||
| `candidates_before_filter` | int\|null | LSH 1차 필터 통과 후보 수(내부 지표) | — |
|
||||
| `engine_version` | string | 엔진 버전 | 로깅 |
|
||||
| `analyzed_at` | datetime | 분석 시각(UTC) | 로깅 |
|
||||
|
||||
**`review_summary` (저작권 탭 요약 — 그대로 표시)**
|
||||
|
||||
| 필드 | 타입 | 의미 |
|
||||
|---|---|---|
|
||||
| `originality_percent` | int 0~100 | **독창성 %** = 100 − 유사도. 점수 변환(캘리브레이션)을 오투오가 완료해 제공 |
|
||||
| `similarity_percent` | int 0~100 | **유사도 %** (무관한 글은 0~5%로 눌러 표시) |
|
||||
| `similar_sentence_count` | int | **유사 문장 건수** = 임계 초과 매칭 수 |
|
||||
| `compared_count` | int | **대조한 원본(코퍼스) 건수** |
|
||||
| `has_suspicion` | bool | **표절 의심 구간 존재 여부** |
|
||||
| `ai_suspicion_level` | `low/medium/high/unknown` | **AI 생성 의심도**. 미학습/채점 불가는 `unknown` |
|
||||
|
||||
**`ai_generation` (AI 생성 의심도 상세)**
|
||||
|
||||
| 필드 | 타입 | 의미 |
|
||||
|---|---|---|
|
||||
| `suspicion_level` | `low/medium/high` | 의심도 등급 |
|
||||
| `score` | float 0~1 | 참고 점수 |
|
||||
| `available` | bool | 학습 모델 또는 명시적으로 활성화한 baseline으로 채점했는지 |
|
||||
| `is_stub` | bool | `true`면 미검증 휴리스틱 baseline. 학습 모델은 `false` |
|
||||
| `model_version` | string | 학습 아티팩트/특징 버전 추적 |
|
||||
| `note` | string | 안내 문구 |
|
||||
|
||||
> 학습 모델이 없으면 점수를 임의 생성하지 않고 `available=false`, `score=null`,
|
||||
> `suspicion_level=unknown`을 반환한다. 학습 후에도 **출판 승인 게이트나 저자 제재의
|
||||
> 단독 근거로 쓰지 말 것**(사람 검토 우선순위용).
|
||||
|
||||
**`matches[]` (표절 의심 시 채워짐)**
|
||||
|
||||
| 필드 | 타입 | 의미 |
|
||||
|---|---|---|
|
||||
| `source_doc` | string | 매칭된 원본 doc_id |
|
||||
| `source_title` | string\|null | 매칭된 원본 제목 |
|
||||
| `similarity` | float 0~1 | 해당 원본과의 결합 유사도 |
|
||||
| `tags[]` | array | 법령 태그. `{ tag, role, label_ko }` |
|
||||
| `tags[].tag` | string | 태그 코드 (예: `reproduction`) |
|
||||
| `tags[].role` | `primary/secondary` | 주 침해 / 보조 |
|
||||
| `tags[].label_ko` | string | 한글 표기 (예: `복제권`) — 배지에 그대로 사용 |
|
||||
| `case_id` | string\|null | 39종 침해 케이스 ID (예: `A6`) |
|
||||
| `case_title` | string\|null | 케이스 명칭 |
|
||||
| `infringement_type` | string | 후방호환 단일 분류(`copy/transform/plot/character/unknown`) |
|
||||
| `evidence_spans[]` | array | 본문 내 일치 구간 `{ start, end, matched }` — **하이라이트용** |
|
||||
| `score_breakdown` | object | 유사도 분해 `text_sim/lemma_sim/character_sim/motif_sim/lsh_jaccard` |
|
||||
| `partial_signal` | object\|null | 군집화 부분 표절(인물만 교체 등) 분해 — 침해요소 DB 적재용 |
|
||||
|
||||
### `legal_risk` (판례 기반 LLM-as-a-Judge 검토 보조)
|
||||
|
||||
등록된 국내 저작권 판례 545건 중 저작물 유형과 법적 태그가 가까운 Top 5를 먼저
|
||||
선별한다. 운영에서 `USE_LLM_LEGAL_JUDGE=true`이고 OpenAI API 키가 설정되어 있으면,
|
||||
GPT가 탐지 증거와 이 Top 5만 비교해 구조화된 의견을 반환한다. 전체 판례를 매 요청에
|
||||
전송하지 않는다.
|
||||
|
||||
| 필드 | 타입 | 의미 |
|
||||
|---|---|---|
|
||||
| `status` | `review_required/no_registered_corpus_match/insufficient_precedent_data` | 사람 검토 상태. 법적 확정 판정이 아님 |
|
||||
| `risk_level` | `low/medium/high/null` | 규칙 기반 검토 우선순위 |
|
||||
| `precedent_ids[]` | string[] | 규칙 엔진이 검색한 판례 Top 5 사건번호 |
|
||||
| `judgment_method` | `llm/rule_based/rule_fallback` | GPT 성공, 기능 비활성, GPT 실패 후 폴백 구분 |
|
||||
| `llm_verdict` | `likely/unlikely/insufficient_evidence/null` | GPT의 비교 의견. 침해 확정값이 아님 |
|
||||
| `llm_confidence` | float 0~1\|null | GPT 자기평가 값. 통계적 침해 확률이 아님 |
|
||||
| `llm_review_required` | bool\|null | GPT 결과는 항상 `true`; 모델이 사람 검토를 해제할 수 없음 |
|
||||
| `llm_matched_precedent_ids[]` | string[] | GPT가 실제 근거로 선택한 사건번호. Top 5 밖의 ID는 서버가 거부 |
|
||||
| `supporting_reasons[]` | string[] | 의심 의견을 지지하는 근거 |
|
||||
| `counter_reasons[]` | string[] | 반대 근거·판단을 약화하는 사실 |
|
||||
| `missing_factors[]` | string[] | 보호 표현, 의거관계, 권리·허락 등 추가 확인사항 |
|
||||
| `judge_model` | string\|null | 사용한 GPT 모델명 |
|
||||
| `judge_prompt_version` | string\|null | 감사·재현용 프롬프트 버전 |
|
||||
| `judge_note` | string\|null | 폴백 또는 법적 한계 안내 |
|
||||
| `judgment_summary` | string | 검증된 판례 사건번호와 핵심 사유를 결합한 사용자 표시용 의견 |
|
||||
|
||||
`matches[].case_id`는 39종 내부 침해유형 ID이고, `legal_risk.*precedent_ids`는 실제
|
||||
판례 사건번호다. 두 필드를 혼용하지 않는다. UI에서는 `llm_verdict`만 단독 표시하지
|
||||
말고 `judgment_summary`를 첫 문장으로 표시한 뒤 찬반 근거, 누락 요소, 판례 사건번호
|
||||
및 면책 문구를 함께 보여준다. 모델명이나 “GPT 판례 비교” 같은 구현 용어는 사용자용
|
||||
판정 제목으로 표시하지 않는다.
|
||||
|
||||
### `score_semantics` (점수 해석)
|
||||
|
||||
- `combined_score`는 검색 랭킹 점수이며 침해 확률이 아니다.
|
||||
- `provisional=true`이면 임계값이 실데이터로 확정되지 않은 잠정값이다.
|
||||
- `union_coverage`는 질의 전체에서 비중복 일치 구간이 차지하는 비율이다.
|
||||
- `evidence_truncated=true`이면 CPU 상한 때문에 일부 후보의 정밀 증거 계산이 생략됐다.
|
||||
|
||||
---
|
||||
|
||||
## 3. `GET /v1/health` — 엔진 상태
|
||||
|
||||
앱 기동 시 호출해 엔진 준비·코퍼스 규모를 확인한다.
|
||||
|
||||
**응답**
|
||||
|
||||
| 필드 | 타입 | 의미 |
|
||||
|---|---|---|
|
||||
| `status` | `"ok"` | 정상 |
|
||||
| `engine_version` | string | 엔진 버전 |
|
||||
| `corpus_size` | int | 로딩된 대조 원본 수 (`review_summary.compared_count`와 동일) |
|
||||
| `taxonomy_version` | string\|null | 분류체계 버전 |
|
||||
| `autobiography_mode` | bool | 자서전 모드 on/off |
|
||||
| `corpus_documents` | int | 등록된 원천 문서 수 |
|
||||
| `index_backend` | string | 현재 검색 인덱스 구현 |
|
||||
| `ai_model_ready` | bool | 학습된 AI 생성 탐지 모델 준비 여부 |
|
||||
| `precedent_count` | int | 로딩된 판례 수. 현재 운영 데이터는 545건 |
|
||||
|
||||
---
|
||||
|
||||
## 4. `GET /v1/taxonomy` — 법령 태그·케이스 정의
|
||||
|
||||
10종 태그와 39종 케이스 정의를 반환. 세션당 1회 받아 캐시하면, `detect` 응답의
|
||||
`tag`/`case_id`를 화면에 설명과 함께 표시할 수 있다.
|
||||
|
||||
**응답**: `meta_tags_version`, `cases_version`, `meta_tags[]`, `cases[]`
|
||||
- `meta_tags[]` 각 항목: `id`, `label_ko`, `category`, `law_ref`, `scope`, `description`
|
||||
- `cases[]` 각 항목: `case_id`, `title`, `subgroup`, `actor`, `primary_tags[]`, `secondary_tags[]`, `detectable_internal`, `high_risk`, `note`
|
||||
|
||||
---
|
||||
|
||||
## 5. `POST /v1/plagiarism/batch` + `GET /v1/plagiarism/batch/{job_id}` — 배치
|
||||
|
||||
여러 건을 한 번에 검사(비동기). 대량 점검용.
|
||||
|
||||
**등록 요청** `POST /v1/plagiarism/batch`
|
||||
```json
|
||||
{ "items": [ { "doc_id": "e1", "text": "..." }, { "doc_id": "e2", "text": "..." } ],
|
||||
"options": { "threshold": null } }
|
||||
{
|
||||
"doc_id": "episode-002",
|
||||
"copyright": {
|
||||
"originality_percent": 10,
|
||||
"similarity_percent": 90,
|
||||
"compared_count": 31560,
|
||||
"has_suspicion": true,
|
||||
"description": "등록 원문 검색 세그먼트 31,560건과 대조한 결과 표절 의심 구간이 확인되었습니다."
|
||||
},
|
||||
"similar_sentences": {
|
||||
"count": 5,
|
||||
"label": "5건"
|
||||
},
|
||||
"ai_generation_suspicion": {
|
||||
"level": "unknown",
|
||||
"label": "확인 불가"
|
||||
},
|
||||
"legal_judgment": {
|
||||
"status": "suspected",
|
||||
"label": "판례에 비추어 저작권 침해 의심",
|
||||
"summary": "2011고단6934, 2013노232 판례의 판단 기준과 탐지 증거를 비교한 결과, 표현 일치 범위가 커 저작권 침해가 의심되어 추가 검토가 필요합니다.",
|
||||
"precedent_ids": ["2011고단6934", "2013노232"]
|
||||
},
|
||||
"analyzed_at": "2026-08-18T03:01:00Z"
|
||||
}
|
||||
```
|
||||
- `items`: 1~500건. 각 `{ doc_id, text, metadata? }`
|
||||
- 응답(202): `{ job_id, status, total, created_at }`
|
||||
|
||||
**결과 조회** `GET /v1/plagiarism/batch/{job_id}`
|
||||
## 5. 화면 매핑
|
||||
|
||||
| 필드 | 의미 |
|
||||
| 화면 항목 | 응답 경로 | 표시 방법 |
|
||||
|---|---|---|
|
||||
| 독창성 98% | `copyright.originality_percent` | 숫자 뒤 `%` |
|
||||
| 저작권 · 유사도 2% | `copyright.similarity_percent` | 숫자 뒤 `%` |
|
||||
| 대조 결과 설명 | `copyright.description` | 문자열 그대로 표시 |
|
||||
| 유사 문장 0건 | `similar_sentences.label` | 문자열 그대로 표시 |
|
||||
| AI 생성 의심도 낮음 | `ai_generation_suspicion.label` | 문자열 그대로 표시 |
|
||||
| 판례 판단 제목 | `legal_judgment.label` | 의심 또는 검토 필요 시 표시 |
|
||||
| 판례 판단 근거 | `legal_judgment.summary` | 제목 아래 설명으로 표시 |
|
||||
| 근거 판례 | `legal_judgment.precedent_ids[]` | 사건번호 배지로 표시 |
|
||||
|
||||
`compared_count`는 현재 등록된 검색 세그먼트 수다. 원천 PDF 파일 수 또는 외부 공개
|
||||
문서 전체 건수로 바꾸어 표기하지 않는다.
|
||||
|
||||
## 6. 상태값
|
||||
|
||||
### `legal_judgment.status`
|
||||
|
||||
| 값 | 의미 | 권장 UI |
|
||||
|---|---|---|
|
||||
| `suspected` | 판례와 탐지 증거에 비추어 침해 의심 | 빨간색 또는 주의 표시 |
|
||||
| `low` | 현재 증거에서 침해 의심 낮음 | 기본 또는 정상 표시 |
|
||||
| `review_required` | 관련 판례에 따른 추가 확인 필요 | 노란색 검토 표시 |
|
||||
| `unavailable` | 등록 판례가 없어 비교 불가 | 회색 안내 표시 |
|
||||
|
||||
### `ai_generation_suspicion.level`
|
||||
|
||||
| 값 | 한글 라벨 |
|
||||
|---|---|
|
||||
| `status` | `queued/running/completed/failed` |
|
||||
| `total` / `processed` | 전체 / 처리 완료 건수 (진행률) |
|
||||
| `results` | `status=completed`일 때만. `DetectResponse` 배열(§2와 동일 구조) |
|
||||
| `error` | 실패 시 사유 |
|
||||
| `low` | 낮음 |
|
||||
| `medium` | 중간 |
|
||||
| `high` | 높음 |
|
||||
| `unknown` | 확인 불가 |
|
||||
|
||||
> 폴링: 등록 → `job_id`로 `completed` 될 때까지 조회 → `results` 사용.
|
||||
`unknown`을 임의로 `low`로 바꾸지 않는다. 학습 모델이 없거나 채점할 수 없는 경우다.
|
||||
|
||||
---
|
||||
|
||||
## 6. `POST /v1/summary` — 스토리 요약
|
||||
|
||||
**요청**: `{ "text": "...", "ratio": 0.3, "max_sentences": null, "use_abstractive": true }`
|
||||
- `ratio`: 요약 길이 비율(입력 대비). `use_abstractive`: LLM 결합(키 없으면 추출 요약 폴백)
|
||||
|
||||
**응답**: `extractive`(추출 요약), `abstractive`(추상 요약\|null), `final`(최종), `mode`(`extractive/hybrid`), `num_sentences_in/out`
|
||||
|
||||
---
|
||||
|
||||
## 7. 코퍼스 관리 (운영/관리용)
|
||||
|
||||
대조 원본을 등록·삭제. 업로드/삭제 시 인덱스 자동 재빌드.
|
||||
|
||||
| API | 설명 |
|
||||
|---|---|
|
||||
| `GET /v1/corpus` | 목록. `{ total, docs[{doc_id,title,size_bytes,filename}] }` |
|
||||
| `POST /v1/corpus` | JSON 등록 `{ doc_id?(자동), title, text }` → `201` |
|
||||
| `POST /v1/corpus/file` | multipart `.txt` 업로드 (`title`, `doc_id?`, `file`) → `201` |
|
||||
| `DELETE /v1/corpus/{doc_id}` | 삭제 → `204` |
|
||||
|
||||
등록 응답(`201`): `{ doc_id, title, size_bytes, corpus_size_after, rebuilt }`
|
||||
|
||||
---
|
||||
|
||||
## 8. 에러 포맷
|
||||
|
||||
FastAPI 표준. `{ "detail": "메시지" }`
|
||||
|
||||
| 코드 | 상황 |
|
||||
|---|---|
|
||||
| 400 | 잘못된 요청(빈 본문/비 UTF-8 파일 등) |
|
||||
| 404 | 배치 job_id 없음 / 코퍼스 doc_id 없음 |
|
||||
| 409 | 코퍼스 doc_id 중복 |
|
||||
| 422 | 스키마 검증 실패(필드 타입·범위) |
|
||||
| 503 | 분류체계 미로딩 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 빠른 시작 (curl)
|
||||
## 7. 호출 예시
|
||||
|
||||
```bash
|
||||
# 1) 엔진 상태
|
||||
curl https://plagiarism.o2o.kr/v1/health
|
||||
|
||||
# 2) 저작권 탭용 탐지 — 이 응답 1건으로 탭 렌더링
|
||||
curl -X POST https://plagiarism.o2o.kr/v1/plagiarism/detect \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: 운영에서 발급된 키" \
|
||||
-d '{"doc_id":"episode-001","text":"검사할 본문 텍스트...","legal_context":{"work_type":"literary","access_evidence":null,"protected_expression_reviewed":false,"rights_verified":false}}'
|
||||
curl -X POST 'https://plagiarism.o2o.kr/v1/plagiarism/review' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"doc_id": "episode-001",
|
||||
"text": "검사할 에피소드 본문"
|
||||
}'
|
||||
```
|
||||
|
||||
> 개발 서버가 무인증 설정이면 `X-API-Key` 헤더를 생략할 수 있다. 운영 서버의 인증
|
||||
> 적용 여부와 발급된 키를 확인한 뒤 연동한다.
|
||||
```javascript
|
||||
const response = await fetch(
|
||||
"https://plagiarism.o2o.kr/v1/plagiarism/review",
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ doc_id: episodeId, text: episodeText })
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) throw new Error(`저작권 검사 실패: ${response.status}`);
|
||||
const result = await response.json();
|
||||
|
||||
originality.textContent = `${result.copyright.originality_percent}%`;
|
||||
copyrightDescription.textContent = result.copyright.description;
|
||||
similarSentenceCount.textContent = result.similar_sentences.label;
|
||||
aiSuspicion.textContent = result.ai_generation_suspicion.label;
|
||||
legalTitle.textContent = result.legal_judgment.label;
|
||||
legalDescription.textContent = result.legal_judgment.summary;
|
||||
```
|
||||
|
||||
## 8. 오류 처리
|
||||
|
||||
| HTTP | 의미 | 처리 |
|
||||
|---|---|---|
|
||||
| `200` | 검사 완료 | 응답으로 화면 갱신 |
|
||||
| `401` | API 키 누락 또는 불일치 | 인증 설정 확인 |
|
||||
| `422` | 요청 형식 오류 또는 빈 본문 | 입력값 확인 |
|
||||
| `503` | 탐지 또는 판례 검토 기능 사용 불가 | 잠시 후 재시도 |
|
||||
| `500` | 서버 내부 오류 | 오류 로그와 `doc_id` 전달 |
|
||||
|
||||
앱은 검사 중 로딩 상태를 표시하고, 실패하면 직전 성공 결과를 새 결과처럼 표시하지 않는다.
|
||||
|
||||
## 9. 상세 API
|
||||
|
||||
관리자 화면에서 일치 원문, 좌표, 점수 구성 등 상세 증거가 필요할 때만
|
||||
`POST /v1/plagiarism/detect`를 사용한다. 모바일 저작권 탭은 응답 크기가 작은
|
||||
`POST /v1/plagiarism/review`를 사용한다.
|
||||
|
||||
이 결과는 등록 코퍼스와 판례에 기반한 검토 보조 의견이며 법률상 침해 확정이 아니다.
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
> 나누구 앱 **저작권 탭** 연동용 API 계약. 본문 입력 → 표절 탐지 결과 + 저작권 탭
|
||||
> 표시 항목을 반환한다. 본 문서 기준으로 UI를 연동하면 된다.
|
||||
> 엔진: `o2o-plagiarism-api` · 문의: 오투오 양형배
|
||||
> 엔진: `o2o-plagiarism-api`
|
||||
|
||||
---
|
||||
|
||||
@ -20,26 +20,33 @@
|
||||
|
||||
## 1. 저작권 탭 화면 ↔ API 필드 매핑 (핵심)
|
||||
|
||||
나누구 저작권 탭의 각 표시 항목은 아래 필드에서 그대로 읽으면 된다. **별도 계산 불필요**
|
||||
— 독창성 환산(점수 변환)까지 오투오가 완료해 `review_summary`로 제공한다.
|
||||
나누구 저작권 탭의 각 표시 항목은 경량 응답에서 그대로 읽으면 된다. **별도 계산 불필요**
|
||||
— 독창성 환산과 한글 라벨까지 서버가 완료해 제공한다.
|
||||
|
||||
| 화면 표시 | 예시 | API 필드 |
|
||||
|---|---|---|
|
||||
| 독창성 **98%** | 98 | `review_summary.originality_percent` |
|
||||
| 저작권 · 유사도 **2%** | 2 | `review_summary.similarity_percent` |
|
||||
| 유사 문장 **0건** | 0 | `review_summary.similar_sentence_count` |
|
||||
| 나누구 에피소드 **3.5만 건**과 대조 | 35000 | `review_summary.compared_count` |
|
||||
| 표절 의심 구간 **없음** | false | `review_summary.has_suspicion` |
|
||||
| AI 생성 의심도 **낮음** | low | `review_summary.ai_suspicion_level` |
|
||||
| 독창성 **98%** | 98 | `copyright.originality_percent` |
|
||||
| 저작권 · 유사도 **2%** | 2 | `copyright.similarity_percent` |
|
||||
| 유사 문장 **0건** | 0건 | `similar_sentences.label` |
|
||||
| 대조 결과 설명 | 문장 | `copyright.description` |
|
||||
| 표절 의심 구간 **없음** | false | `copyright.has_suspicion` |
|
||||
| AI 생성 의심도 **낮음** | 낮음 | `ai_generation_suspicion.label` |
|
||||
| 판례 판단 | 침해 의심 낮음 | `legal_judgment.label` |
|
||||
| 판례 근거 | 사건번호 포함 문장 | `legal_judgment.summary` |
|
||||
|
||||
> `ai_suspicion_level` 값 매핑: `low`=낮음, `medium`=중간, `high`=높음,
|
||||
> `unknown`=미학습 또는 채점 불가. 학습 모델이 없으면 점수를 만들지 않고
|
||||
> `ai_generation.available=false`, `score=null`을 반환합니다.
|
||||
> UI는 지금 그대로 붙여두면 되고, 정식 구현 시 값만 실제로 바뀝니다.
|
||||
> `ai_generation_suspicion.level` 값은 `low`, `medium`, `high`, `unknown`이며,
|
||||
> 화면은 함께 반환되는 한글 `label`을 그대로 표시한다.
|
||||
|
||||
---
|
||||
|
||||
## 2. 표절 탐지 — `POST /v1/plagiarism/detect`
|
||||
## 2. 저작권 탭 경량 검사 — `POST /v1/plagiarism/review`
|
||||
|
||||
모바일 저작권 탭은 이 API를 사용한다. 내부 상세 탐지 결과 중 화면에 필요한
|
||||
독창성, 유사도, 대조 건수, 유사 문장 수, AI 의심도, 판례 판단만 반환한다.
|
||||
요청 형식은 기존 `POST /v1/plagiarism/detect`와 동일하며 응답 계약은
|
||||
`docs/API_GUIDE_BAIKAL.md`를 따른다.
|
||||
|
||||
## 3. 상세 표절 탐지 — `POST /v1/plagiarism/detect`
|
||||
|
||||
본문 1건을 검사한다.
|
||||
|
||||
@ -167,7 +174,7 @@
|
||||
|
||||
---
|
||||
|
||||
## 3. 부가 엔드포인트
|
||||
## 4. 부가 엔드포인트
|
||||
|
||||
| Method | Path | 용도 |
|
||||
|---|---|---|
|
||||
@ -179,7 +186,7 @@
|
||||
|
||||
---
|
||||
|
||||
## 4. 에러 포맷
|
||||
## 5. 에러 포맷
|
||||
|
||||
FastAPI 표준. HTTP 상태코드 + `detail`.
|
||||
|
||||
@ -196,7 +203,7 @@ FastAPI 표준. HTTP 상태코드 + `detail`.
|
||||
|
||||
---
|
||||
|
||||
## 5. 연동 시 주의
|
||||
## 6. 연동 시 주의
|
||||
|
||||
1. **AI 생성 의심도는 확정값이 아님** — `available`/`is_stub`/`model_version`을 확인하고 사람 검토 우선순위로만 사용할 것.
|
||||
출판 승인/거절 게이트로 쓰지 말 것(오탐 리스크).
|
||||
|
||||
Binary file not shown.
@ -42,6 +42,29 @@ def test_detect_returns_schema():
|
||||
assert "engine_version" in body
|
||||
|
||||
|
||||
def test_copyright_review_returns_only_mobile_tab_fields():
|
||||
with TestClient(app) as client:
|
||||
resp = client.post(
|
||||
"/v1/plagiarism/review",
|
||||
json={"doc_id": "mobile-1", "text": "창밖으로 보이는 숲은 오늘따라 푸르게 보였다."},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
body = resp.json()
|
||||
assert set(body) == {
|
||||
"doc_id", "copyright", "similar_sentences",
|
||||
"ai_generation_suspicion", "legal_judgment", "analyzed_at",
|
||||
}
|
||||
assert 0 <= body["copyright"]["originality_percent"] <= 100
|
||||
assert body["copyright"]["description"]
|
||||
assert body["similar_sentences"]["label"].endswith("건")
|
||||
assert body["ai_generation_suspicion"]["label"] in {
|
||||
"낮음", "중간", "높음", "확인 불가",
|
||||
}
|
||||
assert "matches" not in body
|
||||
assert "extracted_elements" not in body
|
||||
assert "score_semantics" not in body
|
||||
|
||||
|
||||
def test_batch_flow():
|
||||
with TestClient(app) as client:
|
||||
resp = client.post(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user