feat(04-01): add ParseInput and ParseOutput schemas

- ParseInput extends StepInput with url field
- ParseOutput extends StepOutput with platform, content_type, content_id, resolved_url, original_url
- Tests for schema validation and inheritance

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 23:11:44 +08:00
parent 3ad2576aed
commit efddef98d3
2 changed files with 137 additions and 0 deletions
+40
View File
@@ -233,3 +233,43 @@ class ASROutput(StepOutput):
speakers: Optional[List[str]] = Field(
None, description="说话者列表(仅说话者分离时有值)"
)
# =============================================================================
# Parse Schemas (Phase 04)
# =============================================================================
class ParseInput(StepInput):
"""Parse 步骤输入
继承 StepInput 的基础字段,添加 URL 字段。
content_id 和 platform 在解析前是空的,解析后会被填充。
Attributes:
url: 要解析的 URL
content_id: 解析后填充的内容 ID(初始为空)
platform: 解析后填充的平台名称(初始为空)
"""
url: str = Field(..., min_length=1, description="URL to parse")
content_id: str = Field(default="", description="Will be populated after parsing")
platform: str = Field(default="", description="Will be populated after parsing")
class ParseOutput(StepOutput):
"""Parse 步骤输出
继承 StepOutput 的基础字段,添加解析特定字段。
Attributes:
platform: 检测到的平台 (bilibili, zhihu)
content_type: 内容类型 (video, article, answer, zhuanlan)
content_id: 提取的内容 ID
resolved_url: 短链解析后的完整 URL
original_url: 用户提供的原始 URL
"""
platform: str = Field(..., description="Detected platform")
content_type: str = Field(..., description="Content type (video, article, etc.)")
content_id: str = Field(..., description="Extracted content ID")
resolved_url: str = Field(..., description="Resolved URL after short link expansion")
original_url: str = Field(..., description="Original URL provided by user")
+97
View File
@@ -472,3 +472,100 @@ class TestJSONSerialization:
restored = WorkflowState.model_validate_json(json_str)
assert "fetch_metadata" in restored.steps
assert restored.steps["fetch_metadata"].status == StepStatus.COMPLETED
# =============================================================================
# Parse Schemas Tests (Phase 04-01)
# =============================================================================
class TestParseInput:
"""测试 ParseInput Schema"""
def test_parse_input_validates_url_field(self):
"""ParseInput 必须包含 url 字段,且 min_length=1"""
from kb_pipeline.schemas import ParseInput
# 缺少 url
with pytest.raises(ValidationError) as exc_info:
ParseInput()
assert "url" in str(exc_info.value)
# 空 url
with pytest.raises(ValidationError):
ParseInput(url="")
# 有效 url
input_data = ParseInput(url="https://www.bilibili.com/video/BV1xx")
assert input_data.url == "https://www.bilibili.com/video/BV1xx"
def test_parse_input_content_id_default_empty(self):
"""ParseInput content_id 默认为空字符串"""
from kb_pipeline.schemas import ParseInput
input_data = ParseInput(url="https://www.bilibili.com/video/BV1xx")
assert input_data.content_id == ""
def test_parse_input_platform_default_empty(self):
"""ParseInput platform 默认为空字符串"""
from kb_pipeline.schemas import ParseInput
input_data = ParseInput(url="https://www.bilibili.com/video/BV1xx")
assert input_data.platform == ""
class TestParseOutput:
"""测试 ParseOutput Schema"""
def test_parse_output_inherits_step_output(self):
"""ParseOutput 继承 StepOutput 的 success 和 error_message"""
from kb_pipeline.schemas import ParseOutput, StepOutput
output = ParseOutput(
success=True,
platform="bilibili",
content_type="video",
content_id="BV1xx",
resolved_url="https://www.bilibili.com/video/BV1xx",
original_url="https://b23.tv/abc"
)
assert isinstance(output, StepOutput)
assert output.success is True
assert output.error_message is None
def test_parse_output_has_required_fields(self):
"""ParseOutput 包含所有必需字段"""
from kb_pipeline.schemas import ParseOutput
output = ParseOutput(
success=True,
platform="bilibili",
content_type="video",
content_id="BV1xx",
resolved_url="https://www.bilibili.com/video/BV1xx",
original_url="https://b23.tv/abc"
)
assert output.platform == "bilibili"
assert output.content_type == "video"
assert output.content_id == "BV1xx"
assert output.resolved_url == "https://www.bilibili.com/video/BV1xx"
assert output.original_url == "https://b23.tv/abc"
def test_parse_output_with_error(self):
"""ParseOutput 可以表示失败状态"""
from kb_pipeline.schemas import ParseOutput
output = ParseOutput(
success=False,
error_message="Unsupported URL format",
platform="",
content_type="",
content_id="",
resolved_url="",
original_url="https://unknown.com/123"
)
assert output.success is False
assert output.error_message == "Unsupported URL format"