266 lines
9.7 KiB
Markdown
266 lines
9.7 KiB
Markdown
---
|
|
phase: 04-analysis-orchestration
|
|
plan: 01
|
|
type: execute
|
|
wave: 1
|
|
depends_on: []
|
|
files_modified:
|
|
- kb_pipeline/processors/base.py
|
|
- kb_pipeline/processors/parse.py
|
|
- kb_pipeline/processors/__init__.py
|
|
- kb_pipeline/schemas.py
|
|
- tests/test_processors/test_base.py
|
|
- tests/test_processors/test_parse.py
|
|
autonomous: true
|
|
requirements: [MNT-01]
|
|
user_setup: []
|
|
|
|
must_haves:
|
|
truths:
|
|
- "BaseProcessor defines unified interface for all processors"
|
|
- "ParseProcessor correctly parses Bilibili and Zhihu URLs"
|
|
- "All processors inherit from BaseProcessor with consistent process() method"
|
|
artifacts:
|
|
- path: "kb_pipeline/processors/base.py"
|
|
provides: "BaseProcessor abstract class"
|
|
contains: "class BaseProcessor"
|
|
min_lines: 80
|
|
- path: "kb_pipeline/processors/parse.py"
|
|
provides: "ParseProcessor implementation"
|
|
contains: "class ParseProcessor"
|
|
min_lines: 60
|
|
- path: "kb_pipeline/schemas.py"
|
|
provides: "ParseInput/ParseOutput schemas"
|
|
contains: "class ParseInput"
|
|
key_links:
|
|
- from: "kb_pipeline/processors/parse.py"
|
|
to: "kb_pipeline/link_parser.py"
|
|
via: "LinkParser.parse()"
|
|
pattern: "LinkParser\(\)\.parse"
|
|
- from: "kb_pipeline/processors/base.py"
|
|
to: "kb_pipeline/state.py"
|
|
via: "WorkflowStateManager integration"
|
|
pattern: "state_manager\."
|
|
---
|
|
|
|
<objective>
|
|
Create unified BaseProcessor interface and ParseProcessor to establish the foundation for the pipeline architecture.
|
|
|
|
Purpose: Provide consistent processor interface that all steps (parse, download, asr, analyze) will follow, enabling unified state management and error handling.
|
|
Output: BaseProcessor abstract class, ParseProcessor implementation, and related schemas.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@/home/starmen1265/.claude/get-shit-done/workflows/execute-plan.md
|
|
@/home/starmen1265/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/ROADMAP.md
|
|
@.planning/STATE.md
|
|
@.planning/phases/04-analysis-orchestration/04-CONTEXT.md
|
|
@.planning/phases/04-analysis-orchestration/04-RESEARCH.md
|
|
|
|
<interfaces>
|
|
<!-- Key types and contracts from existing codebase. Executor should use these directly. -->
|
|
|
|
From kb_pipeline/schemas.py:
|
|
```python
|
|
class StepStatus(str, Enum):
|
|
PENDING = "pending"
|
|
RUNNING = "running"
|
|
COMPLETED = "completed"
|
|
FAILED = "failed"
|
|
SKIPPED = "skipped"
|
|
|
|
class StepInput(BaseModel):
|
|
content_id: str = Field(..., min_length=1)
|
|
platform: Literal["bilibili", "zhihu"]
|
|
|
|
class StepOutput(BaseModel):
|
|
success: bool
|
|
error_message: Optional[str] = None
|
|
completed_at: datetime = Field(default_factory=datetime.now)
|
|
|
|
class WorkflowState(BaseModel):
|
|
def is_step_completed(self, step_name: str) -> bool: ...
|
|
def mark_step_started(self, step_name: str) -> None: ...
|
|
def mark_step_completed(self, step_name: str, output_summary: Optional[str] = None) -> None: ...
|
|
def mark_step_failed(self, step_name: str, error_message: str) -> None: ...
|
|
```
|
|
|
|
From kb_pipeline/state.py:
|
|
```python
|
|
class WorkflowStateManager:
|
|
STATE_FILENAME = "workflow_state.json"
|
|
def __init__(self, content_dir: Path): ...
|
|
def load(self) -> Optional[WorkflowState]: ...
|
|
def save(self, state: WorkflowState) -> None: ...
|
|
def get_or_create(self, content_id: str, platform: str, source_url: str) -> WorkflowState: ...
|
|
```
|
|
|
|
From kb_pipeline/link_parser.py:
|
|
```python
|
|
@dataclass
|
|
class ParsedLink:
|
|
platform: str
|
|
content_type: str
|
|
content_id: str
|
|
original_url: str
|
|
resolved_url: str = ""
|
|
|
|
class LinkParser:
|
|
def parse(self, url: str) -> Optional[ParsedLink]: ...
|
|
```
|
|
|
|
From kb_pipeline/processors/asr.py (existing pattern reference):
|
|
```python
|
|
class ASRProcessor:
|
|
STEP_NAME = "asr"
|
|
def __init__(self, state_manager: Optional[WorkflowStateManager] = None):
|
|
self.settings = get_settings()
|
|
self.state_manager = state_manager
|
|
async def process(self, input_data: ASRInput) -> ASROutput: ...
|
|
```
|
|
</interfaces>
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 1: Define BaseProcessor abstract class</name>
|
|
<files>kb_pipeline/processors/base.py, tests/test_processors/test_base.py</files>
|
|
<behavior>
|
|
- Test 1: BaseProcessor cannot be instantiated directly (abstract)
|
|
- Test 2: Subclass must implement process() method
|
|
- Test 3: BaseProcessor provides state_manager attribute
|
|
- Test 4: BaseProcessor provides STEP_NAME class attribute
|
|
</behavior>
|
|
<action>
|
|
Create `kb_pipeline/processors/base.py` with BaseProcessor abstract class:
|
|
|
|
1. Define abstract base class with:
|
|
- STEP_NAME: str class attribute (must be overridden by subclass)
|
|
- __init__(self, state_manager: Optional[WorkflowStateManager] = None)
|
|
- settings: KBSettings from get_settings()
|
|
- @abstractmethod process(self, input_data: StepInput) -> StepOutput
|
|
|
|
2. Use ABC (Abstract Base Class) from abc module
|
|
3. Import types from typing: Optional, TypeVar, Generic
|
|
4. Keep it minimal - no complex template methods, just the interface contract
|
|
|
|
Reference existing ASRProcessor pattern but extract the common interface.
|
|
Do NOT add retry logic - that belongs in concrete implementations.
|
|
Do NOT add complex inheritance hierarchies - keep it flat and simple.
|
|
</action>
|
|
<verify>
|
|
<automated>pytest tests/test_processors/test_base.py -v -x</automated>
|
|
</verify>
|
|
<done>BaseProcessor abstract class exists with process() method, cannot be instantiated directly</done>
|
|
</task>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 2: Add ParseInput and ParseOutput schemas</name>
|
|
<files>kb_pipeline/schemas.py, tests/test_pipeline/test_schemas.py</files>
|
|
<behavior>
|
|
- Test 1: ParseInput validates url field (required, min_length=1)
|
|
- Test 2: ParseOutput contains platform, content_type, content_id, resolved_url
|
|
- Test 3: ParseOutput inherits success and error_message from StepOutput
|
|
</behavior>
|
|
<action>
|
|
Add ParseInput and ParseOutput schemas to `kb_pipeline/schemas.py`:
|
|
|
|
```python
|
|
class ParseInput(StepInput):
|
|
"""Parse step input - URL to parse"""
|
|
url: str = Field(..., min_length=1, description="URL to parse")
|
|
# Override content_id and platform as optional for parse step
|
|
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 step output"""
|
|
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")
|
|
```
|
|
|
|
Place after ASROutput schema (around line 236).
|
|
Add tests to existing test_schemas.py file.
|
|
</action>
|
|
<verify>
|
|
<automated>pytest tests/test_pipeline/test_schemas.py -v -x -k "Parse"</automated>
|
|
</verify>
|
|
<done>ParseInput and ParseOutput schemas exist and validate correctly</done>
|
|
</task>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 3: Implement ParseProcessor with __init__.py export</name>
|
|
<files>kb_pipeline/processors/parse.py, kb_pipeline/processors/__init__.py, tests/test_processors/test_parse.py</files>
|
|
<behavior>
|
|
- Test 1: ParseProcessor parses Bilibili video URL correctly
|
|
- Test 2: ParseProcessor parses Zhihu answer URL correctly
|
|
- Test 3: ParseProcessor returns ParseOutput with success=False for unknown URLs
|
|
- Test 4: ParseProcessor records state when state_manager is provided
|
|
- Test 5: ParseProcessor can be imported from kb_pipeline.processors
|
|
</behavior>
|
|
<action>
|
|
Create `kb_pipeline/processors/parse.py`:
|
|
|
|
1. Import BaseProcessor from .base, LinkParser from kb_pipeline.link_parser
|
|
2. Import ParseInput, ParseOutput from kb_pipeline.schemas
|
|
3. Implement ParseProcessor(BaseProcessor):
|
|
- STEP_NAME = "parse"
|
|
- async def process(self, input_data: ParseInput) -> ParseOutput:
|
|
a. Create LinkParser instance
|
|
b. Call parser.parse(input_data.url)
|
|
c. If parsed is None, return ParseOutput(success=False, error_message="Unsupported URL")
|
|
d. If parsed, return ParseOutput with parsed fields
|
|
e. Record state on success (if state_manager exists)
|
|
|
|
4. Keep implementation simple - just wrap LinkParser
|
|
5. Handle both success and failure cases with proper state recording
|
|
|
|
Then update `kb_pipeline/processors/__init__.py` to export new classes:
|
|
|
|
```python
|
|
from .base import BaseProcessor
|
|
from .parse import ParseProcessor
|
|
from .asr import ASRProcessor
|
|
|
|
__all__ = ["BaseProcessor", "ParseProcessor", "ASRProcessor"]
|
|
```
|
|
|
|
Keep existing ASRProcessor export and add new ones.
|
|
</action>
|
|
<verify>
|
|
<automated>pytest tests/test_processors/test_parse.py -v -x && python -c "from kb_pipeline.processors import BaseProcessor, ParseProcessor, ASRProcessor; print('OK')"</automated>
|
|
</verify>
|
|
<done>ParseProcessor correctly parses URLs, returns structured ParseOutput, and is exported from processors module</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
- All tests pass: `pytest tests/test_processors/test_base.py tests/test_processors/test_parse.py -v`
|
|
- BaseProcessor is abstract and cannot be instantiated
|
|
- ParseProcessor correctly wraps LinkParser
|
|
- State recording works when state_manager is provided
|
|
- All processors can be imported from kb_pipeline.processors
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- BaseProcessor abstract class defines unified interface
|
|
- ParseProcessor inherits from BaseProcessor
|
|
- ParseInput/ParseOutput schemas exist and validate
|
|
- All tests pass
|
|
- Code follows existing patterns from ASRProcessor
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/04-analysis-orchestration/04-01-SUMMARY.md`
|
|
</output>
|