2026-05-22 21:52:18 +02:00
|
|
|
"""
|
2026-05-22 22:09:28 +02:00
|
|
|
POST /api/planning/exercise-suggest — planungsgebundene Übungssuche (Hybrid + Profil + optional LLM-Rerank).
|
2026-05-22 21:52:18 +02:00
|
|
|
"""
|
|
|
|
|
from fastapi import APIRouter, Depends
|
|
|
|
|
|
|
|
|
|
from db import get_db, get_cursor
|
|
|
|
|
from tenant_context import TenantContext, get_tenant_context
|
|
|
|
|
from planning_exercise_suggest import PlanningExerciseSuggestRequest, suggest_planning_exercises
|
2026-05-23 11:46:25 +02:00
|
|
|
from planning_exercise_path_builder import ProgressionPathSuggestRequest, suggest_progression_path
|
2026-06-06 21:10:52 +02:00
|
|
|
from account_lifecycle import assert_min_account_state
|
|
|
|
|
from capabilities import probe_capability
|
2026-06-07 10:32:49 +02:00
|
|
|
from club_features import (
|
|
|
|
|
consume_club_feature_with_usage,
|
|
|
|
|
merge_feature_usage_into_response,
|
|
|
|
|
probe_club_feature_access,
|
|
|
|
|
resolve_club_id_for_probe,
|
|
|
|
|
)
|
2026-05-22 21:52:18 +02:00
|
|
|
|
|
|
|
|
router = APIRouter(prefix="/api/planning", tags=["planning_exercise_suggest"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/exercise-suggest")
|
|
|
|
|
def post_planning_exercise_suggest(
|
|
|
|
|
body: PlanningExerciseSuggestRequest,
|
|
|
|
|
tenant: TenantContext = Depends(get_tenant_context),
|
|
|
|
|
):
|
2026-06-07 07:43:35 +02:00
|
|
|
uses_ai = body.include_llm_intent or body.include_llm_rank
|
|
|
|
|
club_id = resolve_club_id_for_probe(tenant) if uses_ai else None
|
|
|
|
|
if uses_ai:
|
2026-06-06 21:10:52 +02:00
|
|
|
assert_min_account_state(tenant, "active_member", endpoint="POST /planning/exercise-suggest")
|
|
|
|
|
probe_capability(
|
|
|
|
|
tenant,
|
|
|
|
|
"planning.ai.suggest",
|
|
|
|
|
action="planning_suggest",
|
2026-06-07 07:43:35 +02:00
|
|
|
club_id=club_id,
|
2026-06-06 21:10:52 +02:00
|
|
|
endpoint="POST /planning/exercise-suggest",
|
|
|
|
|
)
|
2026-06-06 21:00:42 +02:00
|
|
|
probe_club_feature_access(
|
|
|
|
|
feature_id="ai_calls",
|
|
|
|
|
action="planning_suggest",
|
2026-06-07 07:43:35 +02:00
|
|
|
club_id=club_id,
|
2026-06-06 21:00:42 +02:00
|
|
|
profile_id=tenant.profile_id,
|
2026-06-07 07:43:35 +02:00
|
|
|
portal_role=tenant.global_role,
|
2026-06-06 21:00:42 +02:00
|
|
|
endpoint="POST /planning/exercise-suggest",
|
2026-06-07 15:47:49 +02:00
|
|
|
tenant=tenant,
|
2026-06-06 21:00:42 +02:00
|
|
|
)
|
2026-05-22 21:52:18 +02:00
|
|
|
with get_db() as conn:
|
|
|
|
|
cur = get_cursor(conn)
|
2026-06-07 07:43:35 +02:00
|
|
|
result = suggest_planning_exercises(cur, tenant=tenant, body=body)
|
|
|
|
|
if uses_ai:
|
2026-06-07 10:32:49 +02:00
|
|
|
usage = consume_club_feature_with_usage(
|
2026-06-07 07:43:35 +02:00
|
|
|
feature_id="ai_calls",
|
|
|
|
|
club_id=club_id,
|
|
|
|
|
profile_id=tenant.profile_id,
|
|
|
|
|
portal_role=tenant.global_role,
|
|
|
|
|
action="planning_suggest",
|
2026-06-07 10:32:49 +02:00
|
|
|
cur=cur,
|
|
|
|
|
tenant=tenant,
|
2026-06-07 07:43:35 +02:00
|
|
|
conn=conn,
|
|
|
|
|
)
|
2026-06-07 10:32:49 +02:00
|
|
|
result = merge_feature_usage_into_response(result, usage)
|
2026-06-07 07:43:35 +02:00
|
|
|
return result
|
2026-05-23 11:46:25 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/progression-path-suggest")
|
|
|
|
|
def post_progression_path_suggest(
|
|
|
|
|
body: ProgressionPathSuggestRequest,
|
|
|
|
|
tenant: TenantContext = Depends(get_tenant_context),
|
|
|
|
|
):
|
2026-06-07 07:43:35 +02:00
|
|
|
uses_ai = (
|
2026-06-06 21:00:42 +02:00
|
|
|
body.include_llm_intent
|
|
|
|
|
or body.include_llm_path_qa
|
|
|
|
|
or body.include_ai_gap_fill
|
2026-06-07 07:43:35 +02:00
|
|
|
)
|
|
|
|
|
club_id = resolve_club_id_for_probe(tenant) if uses_ai else None
|
|
|
|
|
if uses_ai:
|
2026-06-06 21:10:52 +02:00
|
|
|
assert_min_account_state(
|
|
|
|
|
tenant, "active_member", endpoint="POST /planning/progression-path-suggest"
|
|
|
|
|
)
|
|
|
|
|
probe_capability(
|
|
|
|
|
tenant,
|
|
|
|
|
"planning.ai.progression_path",
|
|
|
|
|
action="progression_path_suggest",
|
2026-06-07 07:43:35 +02:00
|
|
|
club_id=club_id,
|
2026-06-06 21:10:52 +02:00
|
|
|
endpoint="POST /planning/progression-path-suggest",
|
|
|
|
|
)
|
2026-06-06 21:00:42 +02:00
|
|
|
probe_club_feature_access(
|
|
|
|
|
feature_id="ai_calls",
|
|
|
|
|
action="progression_path_suggest",
|
2026-06-07 07:43:35 +02:00
|
|
|
club_id=club_id,
|
2026-06-06 21:00:42 +02:00
|
|
|
profile_id=tenant.profile_id,
|
2026-06-07 07:43:35 +02:00
|
|
|
portal_role=tenant.global_role,
|
2026-06-06 21:00:42 +02:00
|
|
|
endpoint="POST /planning/progression-path-suggest",
|
2026-06-07 15:47:49 +02:00
|
|
|
tenant=tenant,
|
2026-06-06 21:00:42 +02:00
|
|
|
)
|
2026-05-23 11:46:25 +02:00
|
|
|
with get_db() as conn:
|
|
|
|
|
cur = get_cursor(conn)
|
2026-06-07 07:43:35 +02:00
|
|
|
result = suggest_progression_path(cur, tenant=tenant, body=body)
|
|
|
|
|
if uses_ai:
|
2026-06-07 10:32:49 +02:00
|
|
|
usage = consume_club_feature_with_usage(
|
2026-06-07 07:43:35 +02:00
|
|
|
feature_id="ai_calls",
|
|
|
|
|
club_id=club_id,
|
|
|
|
|
profile_id=tenant.profile_id,
|
|
|
|
|
portal_role=tenant.global_role,
|
|
|
|
|
action="progression_path_suggest",
|
2026-06-07 10:32:49 +02:00
|
|
|
cur=cur,
|
|
|
|
|
tenant=tenant,
|
2026-06-07 07:43:35 +02:00
|
|
|
conn=conn,
|
|
|
|
|
)
|
2026-06-07 10:32:49 +02:00
|
|
|
result = merge_feature_usage_into_response(result, usage)
|
2026-06-07 07:43:35 +02:00
|
|
|
return result
|