feat(US5+6): QA generation — POST /api/v1/qa/gen-text and /gen-image
- Add qa_models.py with TextQAItem, GenTextQARequest, QAPair, ImageQAItem,
GenImageQARequest, ImageQAPair, TextQAResponse, ImageQAResponse
- Implement gen_text_qa(): batch-formats triples into a single prompt, calls
llm.chat(), parses JSON array via extract_json()
- Implement gen_image_qa(): downloads cropped image from source-data bucket,
base64-encodes inline (data URI), builds multimodal message, calls
llm.chat_vision(), parses JSON; image_path preserved on ImageQAPair
- Replace qa.py stub with full router: POST /qa/gen-text and /qa/gen-image
using Depends(get_llm_client) and Depends(get_storage_client)
- 15 new tests (8 service + 7 router), 53/53 total passing
2026-04-10 16:05:49 +08:00
|
|
|
from fastapi import APIRouter, Depends
|
|
|
|
|
|
|
|
|
|
from app.clients.llm.base import LLMClient
|
|
|
|
|
from app.clients.storage.base import StorageClient
|
|
|
|
|
from app.core.dependencies import get_llm_client, get_storage_client
|
|
|
|
|
from app.models.qa_models import (
|
|
|
|
|
GenImageQARequest,
|
|
|
|
|
GenTextQARequest,
|
|
|
|
|
ImageQAResponse,
|
|
|
|
|
TextQAResponse,
|
|
|
|
|
)
|
|
|
|
|
from app.services import qa_service
|
feat: Phase 1+2 — project setup and core infrastructure
- requirements.txt, config.yaml, .env, Dockerfile, docker-compose.yml
- app/core: config (YAML+env override), logging (JSON structured),
exceptions (typed hierarchy), json_utils (Markdown fence stripping)
- app/clients: LLMClient ABC + ZhipuAIClient (run_in_executor),
StorageClient ABC + RustFSClient (boto3 head_object for size check)
- app/main.py: FastAPI app with health endpoint and router registration
- app/core/dependencies.py: lru_cache singleton factories
- tests/conftest.py: mock_llm, mock_storage, test_app, client fixtures
- pytest.ini: asyncio_mode=auto
- 11 unit tests passing
2026-04-10 15:22:45 +08:00
|
|
|
|
|
|
|
|
router = APIRouter(tags=["QA"])
|
feat(US5+6): QA generation — POST /api/v1/qa/gen-text and /gen-image
- Add qa_models.py with TextQAItem, GenTextQARequest, QAPair, ImageQAItem,
GenImageQARequest, ImageQAPair, TextQAResponse, ImageQAResponse
- Implement gen_text_qa(): batch-formats triples into a single prompt, calls
llm.chat(), parses JSON array via extract_json()
- Implement gen_image_qa(): downloads cropped image from source-data bucket,
base64-encodes inline (data URI), builds multimodal message, calls
llm.chat_vision(), parses JSON; image_path preserved on ImageQAPair
- Replace qa.py stub with full router: POST /qa/gen-text and /qa/gen-image
using Depends(get_llm_client) and Depends(get_storage_client)
- 15 new tests (8 service + 7 router), 53/53 total passing
2026-04-10 16:05:49 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/qa/gen-text", response_model=TextQAResponse)
|
|
|
|
|
async def gen_text_qa(
|
|
|
|
|
req: GenTextQARequest,
|
|
|
|
|
llm: LLMClient = Depends(get_llm_client),
|
|
|
|
|
) -> TextQAResponse:
|
|
|
|
|
return await qa_service.gen_text_qa(req, llm)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/qa/gen-image", response_model=ImageQAResponse)
|
|
|
|
|
async def gen_image_qa(
|
|
|
|
|
req: GenImageQARequest,
|
|
|
|
|
llm: LLMClient = Depends(get_llm_client),
|
|
|
|
|
storage: StorageClient = Depends(get_storage_client),
|
|
|
|
|
) -> ImageQAResponse:
|
|
|
|
|
return await qa_service.gen_image_qa(req, llm, storage)
|