appsmith-api / api /intake.py
Kakashiix26's picture
multiframework: framework in intake/generate/projects
9c61e67 verified
Raw
History Blame Contribute Delete
2.75 kB
"""
IntakeAgent endpoint β€” POST /api/intake.
Self-gating: decides whether a build prompt is specific enough to build directly.
If not, asks ONE adaptive clarifying question at a time (with selectable options)
until it has enough context, then emits a consolidated build brief.
Stateless: the FE passes the full Q&A history on every call. No DB writes.
"""
import logging
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, Field
from agent.llm import PLANNER_CHAIN, chat_structured
from agent.prompts import intake_prompt
from agent.states import IntakeStep
from auth.dependencies import get_current_user
from db.models import User
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/intake", tags=["intake"])
# ponytail: simple guard β€” never ask more than this many questions before forcing a build.
_MAX_HISTORY = 5
class QAPair(BaseModel):
question: str
answer: str
class IntakeRequest(BaseModel):
prompt: str
history: list[QAPair] = Field(default_factory=list)
session_id: str | None = None # ponytail: kept for backward compat, not used
def _synthesize_brief(prompt: str, history: list[QAPair]) -> str:
"""Fold the original prompt + all answers into a single brief (cap fallback)."""
answers = "; ".join(h.answer.strip() for h in history if h.answer.strip())
return f"{prompt.strip()} ({answers})" if answers else prompt.strip()
@router.post("", response_model=IntakeStep)
def intake(
req: IntakeRequest,
current_user: User = Depends(get_current_user),
) -> IntakeStep:
if not req.prompt.strip():
raise HTTPException(status_code=400, detail="prompt must not be empty")
history = [h.model_dump() for h in req.history]
step = chat_structured(
intake_prompt(req.prompt, history),
IntakeStep,
chain=PLANNER_CHAIN,
# ponytail: 2000 (not 600) so reasoning-model fallbacks have room to emit the JSON
# after their thinking tokens β€” 600 left them returning no JSON.
max_tokens=2000,
)
# HARD CAP: stop asking after enough questions β€” force a build.
# ponytail: simple guard against an indecisive model looping forever.
# Carry the intake's framework decision through the fallback paths (default "react").
if len(req.history) >= _MAX_HISTORY and not step.ready:
brief = step.brief or _synthesize_brief(req.prompt, req.history)
return IntakeStep(ready=True, brief=brief, framework=step.framework)
# If the model says ready but gave no brief, fall back to the prompt.
if step.ready and not (step.brief and step.brief.strip()):
return IntakeStep(ready=True, brief=req.prompt.strip(), framework=step.framework)
return step