test
This commit is contained in:
@@ -72,6 +72,11 @@ def log_job_request_sequence() -> None:
|
||||
def _json_body() -> Dict[str, Any]:
|
||||
return request.get_json(silent=True) or {}
|
||||
|
||||
def _require_ai_enabled() -> Optional[Any]:
|
||||
if CLIENT_MODE != "langchain":
|
||||
return jsonify({"error": "AI is disabled. Set OPENAI_API_KEY to enable AI features."}), 503
|
||||
return None
|
||||
|
||||
|
||||
def _java_url(path: str) -> str:
|
||||
base = JAVA_BACKEND_URL.rstrip("/")
|
||||
@@ -612,6 +617,9 @@ def generate_stream() -> Any:
|
||||
|
||||
@app.route("/api/create/sessions/<session_id>/stream", methods=["GET"])
|
||||
def create_stream(session_id: str) -> Any:
|
||||
disabled = _require_ai_enabled()
|
||||
if disabled:
|
||||
return disabled
|
||||
phase = (request.args.get("phase") or "outline").strip().lower()
|
||||
try:
|
||||
session = _fetch_ai_session(session_id)
|
||||
@@ -749,9 +757,14 @@ def create_stream(session_id: str) -> Any:
|
||||
|
||||
@app.route("/api/create/sessions/<session_id>/fields", methods=["POST"])
|
||||
def fill_fields(session_id: str) -> Any:
|
||||
disabled = _require_ai_enabled()
|
||||
if disabled:
|
||||
return disabled
|
||||
try:
|
||||
logger.info("[AI create] fill_fields session_id=%s", session_id)
|
||||
session = _fetch_ai_session(session_id)
|
||||
except Exception: # noqa: BLE001
|
||||
except Exception as exc: # noqa: BLE001
|
||||
logger.warning("[AI create] fill_fields session lookup failed session_id=%s error=%s", session_id, exc)
|
||||
return jsonify({"error": "Session not found"}), 404
|
||||
|
||||
data = _json_body()
|
||||
|
||||
@@ -23,6 +23,9 @@ os.makedirs(TEMPLATE_DIR, exist_ok=True)
|
||||
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
|
||||
OPENAI_BASE_URL = os.environ.get("OPENAI_BASE_URL")
|
||||
JAVA_BACKEND_URL = os.environ.get("JAVA_BACKEND_URL", "http://localhost:8080")
|
||||
|
||||
if not OPENAI_API_KEY:
|
||||
raise RuntimeError("OPENAI_API_KEY is required to start the AI backend.")
|
||||
# Default to GPT-5.1 for full document generation (smart model).
|
||||
# Allow override via SMART_MODEL or legacy OPENAI_MODEL.
|
||||
SMART_MODEL = os.environ.get("SMART_MODEL") or os.environ.get("OPENAI_MODEL") or "gpt-5.1"
|
||||
|
||||
@@ -103,7 +103,22 @@ function App() {
|
||||
setStyleDraft={workflow.setStyleDraft}
|
||||
applyStyleAndRegenerate={workflow.applyStyleAndRegenerate}
|
||||
onAddPromptInfo={workflow.addPromptForFields}
|
||||
onStageSelect={(nextStage) => workflow.setStage(nextStage)}
|
||||
onStageSelect={(nextStage) => {
|
||||
if (workflow.isGenerating || workflow.isStageLoading) return
|
||||
if (nextStage === 'text' && workflow.stage === 'outline') {
|
||||
workflow.approveOutline()
|
||||
return
|
||||
}
|
||||
if (nextStage === 'styling' && workflow.stage === 'text') {
|
||||
workflow.approveDraft()
|
||||
return
|
||||
}
|
||||
if (nextStage === 'review' && workflow.stage === 'styling') {
|
||||
workflow.saveAndReview()
|
||||
return
|
||||
}
|
||||
workflow.setStage(nextStage)
|
||||
}}
|
||||
imagePlaceholdersCount={workflow.imagePlaceholdersCount}
|
||||
isAssetUploading={workflow.isAssetUploading}
|
||||
assetError={workflow.assetError}
|
||||
|
||||
@@ -900,6 +900,11 @@ export function useDocumentWorkflow() {
|
||||
|
||||
const fillFieldsFromAI = useCallback(async (extraPrompt?: string) => {
|
||||
if (!_aiSessionId) return
|
||||
console.debug('[AI create] fillFieldsFromAI start', {
|
||||
sessionId: _aiSessionId,
|
||||
hasExtraPrompt: Boolean(extraPrompt?.trim()),
|
||||
outlineCount: outlineRows.length,
|
||||
})
|
||||
setIsStageLoading(true)
|
||||
try {
|
||||
const res = await fetch(`/api/v1/ai/create/sessions/${_aiSessionId}/fields`, {
|
||||
@@ -914,6 +919,19 @@ export function useDocumentWorkflow() {
|
||||
}),
|
||||
})
|
||||
const data = await res.json().catch(() => ({}))
|
||||
if (!res.ok) {
|
||||
if (res.status === 503) {
|
||||
addMessage('assistant', data.error || 'AI is disabled. Set an API key to enable AI features.')
|
||||
autoFilledRef.current = true
|
||||
autoFillAttemptsRef.current = 0
|
||||
return
|
||||
}
|
||||
console.warn('[AI create] fillFieldsFromAI failed', {
|
||||
sessionId: _aiSessionId,
|
||||
status: res.status,
|
||||
data,
|
||||
})
|
||||
}
|
||||
if (!res.ok) {
|
||||
throw new Error(data.error || 'Failed to auto-fill fields')
|
||||
}
|
||||
@@ -950,7 +968,7 @@ export function useDocumentWorkflow() {
|
||||
} finally {
|
||||
setIsStageLoading(false)
|
||||
}
|
||||
}, [_aiSessionId, outlineRows, autoFillFieldsFromPrompt, prompt, stage, view])
|
||||
}, [_aiSessionId, outlineRows, autoFillFieldsFromPrompt, prompt, stage, view, addMessage])
|
||||
|
||||
const addPromptForFields = useCallback(
|
||||
(extraPrompt: string) => {
|
||||
@@ -1085,15 +1103,19 @@ export function useDocumentWorkflow() {
|
||||
setIsGenerating(true)
|
||||
setIsLivePreviewing(true)
|
||||
try {
|
||||
await fetch(`/api/v1/ai/create/sessions/${_aiSessionId}/outline`, {
|
||||
const res = await fetch(`/api/v1/ai/create/sessions/${_aiSessionId}/outline`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ outlineText, constraints: outlineConstraints }),
|
||||
})
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => ({}))
|
||||
throw new Error(data.error || 'Failed to approve outline')
|
||||
}
|
||||
await runDraft(_aiSessionId)
|
||||
} catch (error) {
|
||||
console.error('Outline approval failed:', error)
|
||||
addMessage('assistant', 'Failed to approve outline. Please try again.')
|
||||
addMessage('assistant', error instanceof Error ? error.message : 'Failed to approve outline. Please try again.')
|
||||
} finally {
|
||||
setIsGenerating(false)
|
||||
setIsLivePreviewing(false)
|
||||
@@ -1116,16 +1138,20 @@ export function useDocumentWorkflow() {
|
||||
setIsGenerating(true)
|
||||
setIsLivePreviewing(true)
|
||||
try {
|
||||
await fetch(`/api/v1/ai/create/sessions/${_aiSessionId}/draft`, {
|
||||
const res = await fetch(`/api/v1/ai/create/sessions/${_aiSessionId}/draft`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ draftSections }),
|
||||
})
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => ({}))
|
||||
throw new Error(data.error || 'Failed to approve draft')
|
||||
}
|
||||
setStage('styling')
|
||||
await runPolish(_aiSessionId)
|
||||
} catch (error) {
|
||||
console.error('Draft approval failed:', error)
|
||||
addMessage('assistant', 'Failed to approve draft. Please try again.')
|
||||
addMessage('assistant', error instanceof Error ? error.message : 'Failed to approve draft. Please try again.')
|
||||
} finally {
|
||||
setIsGenerating(false)
|
||||
setIsLivePreviewing(false)
|
||||
@@ -1229,6 +1255,12 @@ export function useDocumentWorkflow() {
|
||||
})
|
||||
|
||||
const createData = await createResponse.json().catch(() => ({}))
|
||||
console.debug('[AI create] create session response', {
|
||||
status: createResponse.status,
|
||||
ok: createResponse.ok,
|
||||
sessionId: createData.sessionId,
|
||||
error: createData.error,
|
||||
})
|
||||
if (!createResponse.ok || !createData.sessionId) {
|
||||
throw new Error(createData.error || 'Failed to create AI session')
|
||||
}
|
||||
|
||||
+7
@@ -49,6 +49,12 @@ public class AiCreateController {
|
||||
}
|
||||
AiCreateSession session =
|
||||
sessionService.createSession(request.prompt(), request.docType(), request.templateId());
|
||||
log.info(
|
||||
"AI create session created sessionId={} userId={} docType={} templateId={}",
|
||||
session.getSessionId(),
|
||||
session.getUserId(),
|
||||
session.getDocType(),
|
||||
session.getTemplateId());
|
||||
return ResponseEntity.ok(new CreateSessionResponse(session.getSessionId()));
|
||||
}
|
||||
|
||||
@@ -122,6 +128,7 @@ public class AiCreateController {
|
||||
public ResponseEntity<StreamingResponseBody> fillFields(
|
||||
@PathVariable String sessionId, HttpServletRequest request) {
|
||||
sessionService.getSessionForCurrentUser(sessionId);
|
||||
log.info("AI create fillFields sessionId={}", sessionId);
|
||||
return proxy("POST", "/api/create/sessions/" + sessionId + "/fields", request, false);
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -9,6 +9,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.proprietary.model.ai.AiCreateSession;
|
||||
import stirling.software.proprietary.model.ai.AiCreateSessionStatus;
|
||||
@@ -17,6 +18,7 @@ import stirling.software.proprietary.service.ai.AiCreateSessionService;
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/ai/create/internal")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class AiCreateInternalController {
|
||||
|
||||
private final AiCreateSessionService sessionService;
|
||||
@@ -24,6 +26,7 @@ public class AiCreateInternalController {
|
||||
@GetMapping("/sessions/{sessionId}")
|
||||
public ResponseEntity<AiCreateController.AiCreateSessionResponse> getSession(
|
||||
@PathVariable String sessionId) {
|
||||
log.info("AI create internal getSession sessionId={}", sessionId);
|
||||
AiCreateSession session = sessionService.getSession(sessionId);
|
||||
return ResponseEntity.ok(AiCreateController.AiCreateSessionResponse.from(session));
|
||||
}
|
||||
@@ -31,6 +34,7 @@ public class AiCreateInternalController {
|
||||
@PostMapping("/sessions/{sessionId}/update")
|
||||
public ResponseEntity<AiCreateController.AiCreateSessionResponse> updateSession(
|
||||
@PathVariable String sessionId, @RequestBody UpdateSessionRequest request) {
|
||||
log.info("AI create internal updateSession sessionId={}", sessionId);
|
||||
AiCreateSession session =
|
||||
sessionService.applyInternalUpdate(
|
||||
sessionId,
|
||||
|
||||
@@ -35,6 +35,7 @@ services:
|
||||
- OPENAI_API_KEY=sk-proj-mVg... etc please insert
|
||||
- SMART_MODEL=gpt-5.1
|
||||
- FAST_MODEL=gpt-4.1-nano
|
||||
- JAVA_BACKEND_URL=http://backend:8080
|
||||
networks:
|
||||
- ai-stack
|
||||
|
||||
|
||||
Reference in New Issue
Block a user