Files
nextcraft/apps/ai-service/pyproject.toml
T
CIAgent 04bdccf189 feat(P05): defense endpoints (Wave 3)
Task 5-3-01: api/defense.py — POST /v1/defense/start (DefenseRecord + first examiner
question persisted + voice descriptor + trace_complete disclosure — the defense does
NOT gate on completeness, the grader does per G-4), POST /{id}/answer ({text} or
multipart audio -> STT via VoiceProvider; follow-up question; per-turn latency
stt_ms/llm_ms/tts_ms), GET /{id}/audio/{turn_id} (streaming TTS WAV), POST /{id}/finish
(DefenseVerdict via D-020 + A-109 integrity signals: long pauses computed from turn
metadata at PAUSE_THRESHOLD_MS=15s), GET /{id} (ordered transcript + signals + status).
Lifespan wires DefenseStore + voice provider + ExaminerAgent. python-multipart dep for
audio parsing.

11 endpoint tests green (full loop mock voice + mock LLM; long-pause signal; 404s; 422
no-body; audio streaming). Suite 378 green; ruff clean.

---ci---
phase: 5
milestone: v0.3
status: execute
requirements: {covered: [REQ-3-006], partial: []}
---/ci---
2026-09-12 04:37:30 +00:00

47 lines
1.2 KiB
TOML

[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"
[project]
name = "nextcraft-ai-service"
version = "0.2.0"
description = "Nextcraft AI tutor service — six LLM agents behind a provider-agnostic layer"
requires-python = ">=3.11"
dependencies = [
"fastapi>=0.141,<0.142",
"uvicorn>=0.52,<0.53",
"pydantic>=2.13,<2.14",
"pydantic-settings>=2.15,<2.16",
"httpx>=0.28,<0.29",
"sse-starlette>=3.4,<3.5",
"sqlmodel>=0.0.24,<0.1",
"sqlalchemy>=2.0,<2.1",
"websockets>=13,<16",
"aiofiles>=24.1,<26",
# POST /v1/defense/{id}/answer multipart audio (REQ-3-006): FastAPI
# form/File parsing requires python-multipart at runtime.
"python-multipart>=0.0.32,<0.1",
]
[project.optional-dependencies]
dev = [
"pytest>=9.1,<10",
"pytest-asyncio>=1.4,<2",
"ruff>=0.14",
]
[tool.setuptools.packages.find]
include = ["ai_service*"]
[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]
[tool.ruff]
line-length = 100
target-version = "py311"
[tool.ruff.lint]
select = ["E", "F", "W", "I", "UP", "B"]
# B008: Depends() in argument defaults is the idiomatic FastAPI DI pattern
ignore = ["B008"]