Implement unified story generation flow
This commit is contained in:
@@ -5,13 +5,23 @@ APP_URL="${APP_URL:-http://localhost:52080}"
|
||||
BACKEND_URL="${BACKEND_URL:-http://localhost:52000}"
|
||||
ADMIN_BACKEND_URL="${ADMIN_BACKEND_URL:-http://localhost:52800}"
|
||||
ADMIN_AUTH="${ADMIN_AUTH:-admin:admin}"
|
||||
DEV_SIGNIN_URL="${DEV_SIGNIN_URL:-$APP_URL/auth/dev/signin}"
|
||||
SMOKE_AUDIO="${SMOKE_AUDIO:-0}"
|
||||
SMOKE_VOICE="${SMOKE_VOICE:-0}"
|
||||
SMOKE_REAL_ASR="${SMOKE_REAL_ASR:-0}"
|
||||
REAL_ASR_AUDIO_FILE="${REAL_ASR_AUDIO_FILE:-}"
|
||||
REAL_ASR_EXPECTED_TEXT="${REAL_ASR_EXPECTED_TEXT:-小熊和星星一起找家}"
|
||||
REAL_ASR_DURATION_MS="${REAL_ASR_DURATION_MS:-2200}"
|
||||
|
||||
if [[ "$SMOKE_REAL_ASR" == "1" ]]; then
|
||||
SMOKE_VOICE=1
|
||||
fi
|
||||
|
||||
COOKIE_JAR="$(mktemp "${TMPDIR:-/tmp}/dreamweaver-cookie.XXXXXX")"
|
||||
VOICE_SMOKE_AUDIO="$(mktemp "${TMPDIR:-/tmp}/dreamweaver-voice-audio.XXXXXX")"
|
||||
REAL_ASR_SMOKE_AUDIO="${TMPDIR:-/tmp}/dreamweaver-real-asr-audio.$$.$RANDOM.m4a"
|
||||
cleanup() {
|
||||
rm -f "$COOKIE_JAR" "$VOICE_SMOKE_AUDIO"
|
||||
rm -f "$COOKIE_JAR" "$VOICE_SMOKE_AUDIO" "$REAL_ASR_SMOKE_AUDIO" "$REAL_ASR_SMOKE_AUDIO.caf"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
@@ -57,6 +67,78 @@ assert_jq() {
|
||||
fi
|
||||
}
|
||||
|
||||
curl_form_capture() {
|
||||
local body_file="$1"
|
||||
local status_file="$2"
|
||||
local url="$3"
|
||||
shift 3
|
||||
|
||||
local http_code
|
||||
if http_code="$(curl -sS -b "$COOKIE_JAR" -o "$body_file" -w '%{http_code}' "$@" "$url")"; then
|
||||
printf '%s' "$http_code" > "$status_file"
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf '%s' "${http_code:-curl_failed}" > "$status_file"
|
||||
return 1
|
||||
}
|
||||
|
||||
print_json_or_raw() {
|
||||
local body_file="$1"
|
||||
if jq '.' "$body_file" >&2 2>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
cat "$body_file" >&2
|
||||
}
|
||||
|
||||
print_real_asr_diagnostics() {
|
||||
local session_id="$1"
|
||||
local body_file="$2"
|
||||
|
||||
echo "Real ASR smoke failed." >&2
|
||||
echo "Required backend env: ASR_PROVIDERS=[\"openai_asr\"] or [\"openai_asr\", \"demo\"], OPENAI_API_KEY, optional OPENAI_API_BASE, VOICE_TRANSCRIPTION_MODEL, VOICE_TRANSCRIPTION_LANGUAGE." >&2
|
||||
echo "Upload response:" >&2
|
||||
print_json_or_raw "$body_file"
|
||||
|
||||
if [[ -n "$session_id" && "$session_id" != "null" ]]; then
|
||||
echo "Voice session events:" >&2
|
||||
if voice_diag_json="$(get_json "$APP_URL/api/voice-sessions/$session_id" 2>/dev/null)"; then
|
||||
echo "$voice_diag_json" | jq '{id,status,last_error,events:[.events[] | {event_type,status,message,event_metadata}]}' >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Admin ASR analytics:" >&2
|
||||
if admin_asr_json="$(curl -fsS -u "$ADMIN_AUTH" "$ADMIN_BACKEND_URL/admin/providers/analytics?days=7&capability=asr" 2>/dev/null)"; then
|
||||
echo "$admin_asr_json" | jq '{capability,total_calls,successful_calls,failed_calls,voice_session_count,voice_turn_count,by_provider,failure_reasons}' >&2
|
||||
fi
|
||||
|
||||
echo "If provider rows were changed in Admin, POST /admin/providers/reload and restart the API container/process before rerunning this smoke." >&2
|
||||
}
|
||||
|
||||
ensure_real_asr_audio() {
|
||||
if [[ -n "$REAL_ASR_AUDIO_FILE" ]]; then
|
||||
if [[ ! -f "$REAL_ASR_AUDIO_FILE" ]]; then
|
||||
echo "REAL_ASR_AUDIO_FILE does not exist: $REAL_ASR_AUDIO_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
printf '%s\n' "$REAL_ASR_AUDIO_FILE"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if command -v say >/dev/null 2>&1 && command -v afconvert >/dev/null 2>&1; then
|
||||
if ! say -v Tingting -o "$REAL_ASR_SMOKE_AUDIO.caf" "$REAL_ASR_EXPECTED_TEXT" 2>/dev/null; then
|
||||
say -o "$REAL_ASR_SMOKE_AUDIO.caf" "$REAL_ASR_EXPECTED_TEXT"
|
||||
fi
|
||||
afconvert -f m4af -d aac "$REAL_ASR_SMOKE_AUDIO.caf" "$REAL_ASR_SMOKE_AUDIO" >/dev/null
|
||||
rm -f "$REAL_ASR_SMOKE_AUDIO.caf"
|
||||
printf '%s\n' "$REAL_ASR_SMOKE_AUDIO"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "SMOKE_REAL_ASR=1 requires REAL_ASR_AUDIO_FILE, or macOS say + afconvert to synthesize a short sample." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
wait_for_job_story() {
|
||||
local job_id="$1"
|
||||
local attempts="${2:-60}"
|
||||
@@ -88,7 +170,7 @@ curl -fsS "$BACKEND_URL/health" | jq -e '.status == "ok"' >/dev/null
|
||||
curl -fsS "$ADMIN_BACKEND_URL/health" | jq -e '.status == "ok"' >/dev/null
|
||||
|
||||
say "Logging in with dev auth"
|
||||
curl -fsS -c "$COOKIE_JAR" -o /dev/null -L "$APP_URL/auth/dev/signin"
|
||||
curl -fsS -c "$COOKIE_JAR" -o /dev/null -L "$DEV_SIGNIN_URL"
|
||||
session_json="$(get_json "$APP_URL/auth/session")"
|
||||
assert_jq "$session_json" '.user.id == "github:dev_user_001"' "dev session should be active"
|
||||
|
||||
@@ -211,6 +293,48 @@ if [[ "$SMOKE_VOICE" == "1" ]]; then
|
||||
voice_waiting_analytics_json="$(get_json "$APP_URL/api/voice-sessions/analytics?days=7&session_status=waiting_user")"
|
||||
assert_jq "$voice_waiting_analytics_json" '.session_status == "waiting_user" and .total_sessions >= 1' "voice analytics should filter by session status"
|
||||
|
||||
if [[ "$SMOKE_REAL_ASR" == "1" ]]; then
|
||||
say "Submitting voice uploaded turn with real OpenAI ASR"
|
||||
real_asr_audio_path="$(ensure_real_asr_audio)"
|
||||
real_asr_body="$(mktemp "${TMPDIR:-/tmp}/dreamweaver-real-asr-body.XXXXXX")"
|
||||
real_asr_status_file="$(mktemp "${TMPDIR:-/tmp}/dreamweaver-real-asr-status.XXXXXX")"
|
||||
if ! curl_form_capture "$real_asr_body" "$real_asr_status_file" "$APP_URL/api/voice-sessions/$voice_session_id/turns" \
|
||||
-F "audio_file=@${real_asr_audio_path};filename=real-asr.m4a;type=audio/mp4" \
|
||||
-F "duration_ms=${REAL_ASR_DURATION_MS}"; then
|
||||
print_real_asr_diagnostics "$voice_session_id" "$real_asr_body"
|
||||
rm -f "$real_asr_body" "$real_asr_status_file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
real_asr_status="$(cat "$real_asr_status_file")"
|
||||
if [[ "$real_asr_status" != "202" ]]; then
|
||||
echo "Unexpected real ASR upload HTTP status: $real_asr_status" >&2
|
||||
print_real_asr_diagnostics "$voice_session_id" "$real_asr_body"
|
||||
rm -f "$real_asr_body" "$real_asr_status_file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
real_asr_upload_json="$(cat "$real_asr_body")"
|
||||
rm -f "$real_asr_body" "$real_asr_status_file"
|
||||
real_asr_turn_id="$(jq -r '.turn_id' <<<"$real_asr_upload_json")"
|
||||
assert_jq "$real_asr_upload_json" '.status != "failed" and .transcription_provider == "openai_asr"' "real ASR upload turn should use openai_asr"
|
||||
|
||||
real_asr_detail_json="$(get_json "$APP_URL/api/voice-sessions/$voice_session_id/turns/$real_asr_turn_id")"
|
||||
assert_jq "$real_asr_detail_json" '.transcription_provider == "openai_asr"' "real ASR turn detail should keep openai_asr provider"
|
||||
assert_jq "$real_asr_detail_json" '.user_transcript != null and (.user_transcript | length) > 0' "real ASR turn should expose a non-empty transcript"
|
||||
assert_jq "$real_asr_detail_json" '.assistant_text != null and .assistant_text != ""' "real ASR turn should continue the narrative"
|
||||
echo "$real_asr_detail_json" | jq '{id,status,transcription_provider,user_transcript,detected_intent,requires_confirmation,assistant_audio_ready,assistant_text}'
|
||||
|
||||
voice_openai_asr_analytics_json="$(get_json "$APP_URL/api/voice-sessions/analytics?days=7&provider=openai_asr")"
|
||||
assert_jq "$voice_openai_asr_analytics_json" '.provider == "openai_asr" and .uploaded_audio_turns >= 1 and (.transcription_provider_counts.openai_asr >= 1)' "voice analytics should filter real ASR provider"
|
||||
|
||||
admin_asr_analytics_json="$(curl -fsS -u "$ADMIN_AUTH" "$ADMIN_BACKEND_URL/admin/providers/analytics?days=7&capability=asr")"
|
||||
assert_jq "$admin_asr_analytics_json" '.capability == "asr" and .successful_calls >= 1 and ([.by_provider[].adapter] | index("openai_asr")) != null' "admin ASR analytics should include openai_asr"
|
||||
echo "$admin_asr_analytics_json" | jq '{capability,total_calls,successful_calls,failed_calls,voice_session_count,voice_turn_count,by_provider,failure_reasons}'
|
||||
else
|
||||
say "Skipping real ASR smoke; set SMOKE_REAL_ASR=1 with backend OPENAI_API_KEY and ASR_PROVIDERS=[\"openai_asr\", \"demo\"]"
|
||||
fi
|
||||
|
||||
say "Finalizing voice session into story"
|
||||
voice_finalize_json="$(post_json "$APP_URL/api/voice-sessions/$voice_session_id/finalize" '{
|
||||
"save_story": true,
|
||||
|
||||
Reference in New Issue
Block a user