chore: dynamic UI sprint update and repo hygiene
This commit is contained in:
@@ -2,13 +2,17 @@ import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { execSync } from 'node:child_process';
|
||||
|
||||
const targetSiteUrl =
|
||||
process.env.PUBLIC_SITE_URL ||
|
||||
process.env.EXTERNAL_SITE_URL ||
|
||||
'https://electron-rare.github.io/';
|
||||
const rawSiteUrl = process.env.PUBLIC_SITE_URL || process.env.EXTERNAL_SITE_URL;
|
||||
if (!rawSiteUrl) {
|
||||
console.error(
|
||||
'[build-astro-external] Missing PUBLIC_SITE_URL (or EXTERNAL_SITE_URL). Refusing external build with fallback canonical.'
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const normalizeUrl = (value) => (value.endsWith('/') ? value : `${value}/`);
|
||||
process.env.PUBLIC_SITE_URL = normalizeUrl(targetSiteUrl);
|
||||
process.env.PUBLIC_SITE_URL = normalizeUrl(rawSiteUrl);
|
||||
const siteUrl = process.env.PUBLIC_SITE_URL;
|
||||
|
||||
const run = (command) => {
|
||||
execSync(command, {
|
||||
@@ -33,4 +37,11 @@ if (fs.existsSync(labSource)) {
|
||||
console.warn('[build-astro-external] Warning: lab/ not found; skipping lab route packaging.');
|
||||
}
|
||||
|
||||
const robotsContent = `User-agent: *\nAllow: /\n\nSitemap: ${siteUrl}sitemap.xml\n`;
|
||||
const sitemapContent = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n <url>\n <loc>${siteUrl}</loc>\n <changefreq>weekly</changefreq>\n <priority>1.0</priority>\n </url>\n <url>\n <loc>${siteUrl}lab/</loc>\n <changefreq>weekly</changefreq>\n <priority>0.8</priority>\n </url>\n</urlset>\n`;
|
||||
|
||||
fs.writeFileSync(path.resolve(process.cwd(), 'dist', 'robots.txt'), robotsContent, 'utf8');
|
||||
fs.writeFileSync(path.resolve(process.cwd(), 'dist', 'sitemap.xml'), sitemapContent, 'utf8');
|
||||
console.log('[build-astro-external] Wrote dist/robots.txt and dist/sitemap.xml for external domain.');
|
||||
|
||||
console.log('[build-astro-external] External Astro build done.');
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
const budgetRules = [
|
||||
{
|
||||
path: 'public/assets/da/openai/hero-pcb-routing-map-v2.webp',
|
||||
maxBytes: 350 * 1024
|
||||
},
|
||||
{
|
||||
path: 'public/assets/da/openai/hero-pcb-routing-map-mobile-low-noise-v2.webp',
|
||||
maxBytes: 180 * 1024
|
||||
},
|
||||
{
|
||||
path: 'public/assets/da/openai/hero-instrument-panel-v2.webp',
|
||||
maxBytes: 220 * 1024
|
||||
},
|
||||
{
|
||||
path: 'public/assets/da/openai/hero-instrument-panel-mobile-low-noise-v2.webp',
|
||||
maxBytes: 200 * 1024
|
||||
},
|
||||
{
|
||||
path: 'public/assets/da/openai/hero-carnet-labo-open-v2.webp',
|
||||
maxBytes: 240 * 1024
|
||||
},
|
||||
{
|
||||
path: 'public/assets/da/openai/carnet-labo-ouvert.webp',
|
||||
maxBytes: 240 * 1024
|
||||
}
|
||||
];
|
||||
|
||||
const fmtKB = (bytes) => `${(bytes / 1024).toFixed(1)} KB`;
|
||||
|
||||
const failures = [];
|
||||
for (const rule of budgetRules) {
|
||||
const absPath = path.resolve(process.cwd(), rule.path);
|
||||
if (!fs.existsSync(absPath)) {
|
||||
failures.push(`${rule.path}: missing file`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const size = fs.statSync(absPath).size;
|
||||
if (size > rule.maxBytes) {
|
||||
failures.push(`${rule.path}: ${fmtKB(size)} > budget ${fmtKB(rule.maxBytes)}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (failures.length > 0) {
|
||||
console.error('[image-budget] FAILED');
|
||||
for (const failure of failures) {
|
||||
console.error(`- ${failure}`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`[image-budget] OK (${budgetRules.length} assets within budget)`);
|
||||
Executable
+81
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
LOCAL_DIR="${1:-dist}"
|
||||
FTP_URL="${FTP_URL:-}"
|
||||
FTP_HOST="${FTP_HOST:-ftp.cluster129.hosting.ovh.net}"
|
||||
FTP_USER="${FTP_USER:-ecobsoleiq}"
|
||||
FTP_PORT="${FTP_PORT:-21}"
|
||||
FTP_REMOTE_DIR="${FTP_REMOTE_DIR:-/www}"
|
||||
FTP_ENV_FILE="${FTP_ENV_FILE:-.env.ftp}"
|
||||
FTP_PASS="${FTP_PASS:-}"
|
||||
|
||||
if [[ -z "$FTP_PASS" && -n "${FTP_PASS_FILE:-}" && -f "$FTP_PASS_FILE" ]]; then
|
||||
FTP_PASS="$(tr -d '\r\n' < "$FTP_PASS_FILE")"
|
||||
fi
|
||||
|
||||
if [[ -z "$FTP_PASS" && -f "$FTP_ENV_FILE" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "$FTP_ENV_FILE"
|
||||
fi
|
||||
|
||||
if [[ -z "${FTP_PASS:-}" ]]; then
|
||||
echo "Missing FTP_PASS."
|
||||
echo "Set it with: FTP_PASS='...'"
|
||||
echo "or in: ${FTP_ENV_FILE} (local, non versioned)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "$FTP_URL" ]]; then
|
||||
if [[ "$FTP_URL" =~ ^ftp://([^@:/]+)@([^:/]+)(:([0-9]+))?(/.*)?$ ]]; then
|
||||
FTP_USER="${BASH_REMATCH[1]}"
|
||||
FTP_HOST="${BASH_REMATCH[2]}"
|
||||
FTP_PORT="${BASH_REMATCH[4]:-$FTP_PORT}"
|
||||
if [[ -n "${BASH_REMATCH[5]:-}" ]]; then
|
||||
FTP_REMOTE_DIR="${BASH_REMATCH[5]}"
|
||||
fi
|
||||
else
|
||||
echo "Invalid FTP_URL format. Expected ftp://user@host:21/path"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$FTP_REMOTE_DIR" ]]; then
|
||||
FTP_REMOTE_DIR="/www"
|
||||
fi
|
||||
|
||||
if [[ "$FTP_REMOTE_DIR" != /* ]]; then
|
||||
FTP_REMOTE_DIR="/$FTP_REMOTE_DIR"
|
||||
fi
|
||||
|
||||
if [[ "$FTP_REMOTE_DIR" == "/" && "${ALLOW_FTP_ROOT_SYNC:-0}" != "1" ]]; then
|
||||
echo "Refusing FTP sync to '/'."
|
||||
echo "Set FTP_REMOTE_DIR='/www' (recommended for OVH web hosting)."
|
||||
echo "If you really need root sync, set ALLOW_FTP_ROOT_SYNC=1."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -d "$LOCAL_DIR" ]]; then
|
||||
echo "Local directory not found: $LOCAL_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v lftp >/dev/null 2>&1; then
|
||||
echo "lftp is required. Install via: brew install lftp | apt-get install lftp"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cat <<EOF | lftp -u "$FTP_USER","$FTP_PASS" "$FTP_HOST"
|
||||
set ftp:list-options -a;
|
||||
set cmd:fail-exit true;
|
||||
set net:max-retries 2;
|
||||
set net:timeout 30;
|
||||
set mirror:use-pget-n 4;
|
||||
mirror --reverse --delete \
|
||||
--dereference \
|
||||
--continue \
|
||||
--verbose \
|
||||
"$LOCAL_DIR" "$FTP_REMOTE_DIR"
|
||||
bye
|
||||
EOF
|
||||
Executable
+108
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
LOCAL_DIR="${1:-dist}"
|
||||
FTP_HOST="${FTP_HOST:-ftp.cluster129.hosting.ovh.net}"
|
||||
FTP_USER="${FTP_USER:-ecobsoleiq}"
|
||||
FTP_REMOTE_DIR="${FTP_REMOTE_DIR:-/home/ecobsoleiq/www}"
|
||||
FTP_ENV_FILE="${FTP_ENV_FILE:-.env.ftp}"
|
||||
FTP_PASS="${FTP_PASS:-}"
|
||||
FTP_URL="${FTP_URL:-}"
|
||||
DEPLOY_SFTP_DELETE="${DEPLOY_SFTP_DELETE:-0}"
|
||||
|
||||
if [[ -z "$FTP_PASS" && -n "${FTP_PASS_FILE:-}" && -f "$FTP_PASS_FILE" ]]; then
|
||||
FTP_PASS="$(tr -d '\r\n' < "$FTP_PASS_FILE")"
|
||||
fi
|
||||
|
||||
if [[ -z "$FTP_PASS" && -f "$FTP_ENV_FILE" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "$FTP_ENV_FILE"
|
||||
fi
|
||||
|
||||
if [[ -z "${FTP_PASS:-}" ]]; then
|
||||
echo "Missing FTP_PASS."
|
||||
echo "Set it with: FTP_PASS='...'"
|
||||
echo "or in: ${FTP_ENV_FILE} (local, non versioned)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "$FTP_URL" ]]; then
|
||||
if [[ "$FTP_URL" =~ ^s?ftp://([^@:/]+)@([^:/]+)(:([0-9]+))?(/.*)?$ ]]; then
|
||||
FTP_USER="${BASH_REMATCH[1]}"
|
||||
FTP_HOST="${BASH_REMATCH[2]}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$FTP_REMOTE_DIR" ]]; then
|
||||
FTP_REMOTE_DIR="/home/ecobsoleiq/www"
|
||||
fi
|
||||
|
||||
if [[ ! -d "$LOCAL_DIR" ]]; then
|
||||
echo "Local directory not found: $LOCAL_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v lftp >/dev/null 2>&1; then
|
||||
echo "lftp is required. Install via: brew install lftp | apt-get install lftp"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SYNC_DIRS=("assets" "_astro" "lab")
|
||||
ROOT_FILES=("index.html" "robots.txt" "sitemap.xml" "favicon.ico" "favicon.svg" "favicon-16x16.png" "favicon-32x32.png" "apple-touch-icon.png")
|
||||
|
||||
if [[ "$DEPLOY_SFTP_DELETE" != "1" ]]; then
|
||||
mirror_delete_flag=""
|
||||
else
|
||||
mirror_delete_flag="--delete"
|
||||
fi
|
||||
|
||||
CMD_FILE="$(mktemp)"
|
||||
trap 'rm -f "$CMD_FILE"' EXIT
|
||||
|
||||
cat > "$CMD_FILE" <<'EOF'
|
||||
set net:timeout 120
|
||||
set sftp:connect-program "ssh -a -x -o ConnectTimeout=20 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
|
||||
set cmd:fail-exit true
|
||||
set net:max-retries 6
|
||||
set net:reconnect-interval-base 5
|
||||
set net:reconnect-interval-max 30
|
||||
set mirror:use-pget-n 1
|
||||
set xfer:clobber on
|
||||
EOF
|
||||
printf 'open -u "%s","%s" "sftp://%s"\n' "$FTP_USER" "$FTP_PASS" "$FTP_HOST" >> "$CMD_FILE"
|
||||
|
||||
# Sync known directories first (stable on OVH shared hosting).
|
||||
for sync_dir in "${SYNC_DIRS[@]}"; do
|
||||
local_dir="$LOCAL_DIR/$sync_dir"
|
||||
remote_dir="$FTP_REMOTE_DIR/$sync_dir"
|
||||
if [[ -d "$local_dir" ]]; then
|
||||
printf 'mirror --reverse --dereference --continue --only-newer %s --verbose "%s" "%s"\n' \
|
||||
"$mirror_delete_flag" "$local_dir" "$remote_dir" >> "$CMD_FILE"
|
||||
else
|
||||
echo "Skipping missing directory: $local_dir" >&2
|
||||
fi
|
||||
done
|
||||
|
||||
# Sync root assets one-by-one to avoid long-running full-root mirror timeouts.
|
||||
for root_file in "${ROOT_FILES[@]}"; do
|
||||
if [[ -f "$LOCAL_DIR/$root_file" ]]; then
|
||||
printf 'put -O "%s" "%s"\n' "$FTP_REMOTE_DIR" "$LOCAL_DIR/$root_file" >> "$CMD_FILE"
|
||||
else
|
||||
echo "Skipping missing root file: $LOCAL_DIR/$root_file" >&2
|
||||
fi
|
||||
done
|
||||
echo "bye" >> "$CMD_FILE"
|
||||
|
||||
if [[ "${DEPLOY_SFTP_DRYRUN:-0}" == "1" ]]; then
|
||||
echo "--- lftp command plan ---"
|
||||
cat "$CMD_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ -n "${DEPLOY_SFTP_VERBOSE:-}" ]]; then
|
||||
echo "--- lftp command plan ---"
|
||||
cat "$CMD_FILE"
|
||||
fi
|
||||
|
||||
lftp -f "$CMD_FILE"
|
||||
Executable
+321
@@ -0,0 +1,321 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
TODAY="$(date +%F)"
|
||||
BATCH_FILE="${1:-$ROOT_DIR/tmp/imagegen/mistral/carnet-labo-assets-v2.jsonl}"
|
||||
OUTPUT_DIR="${OUTPUT_DIR:-$ROOT_DIR/output/imagegen/mistral/carnet-labo-assets-v2}"
|
||||
REPORT_FILE="${REPORT_FILE:-$ROOT_DIR/artifacts/imagegen/$TODAY/asset-run-report.md}"
|
||||
|
||||
MISTRAL_IMAGES_ENDPOINT="https://api.mistral.ai/v1/images/generations"
|
||||
MISTRAL_CONVERSATIONS_ENDPOINT="https://api.mistral.ai/v1/conversations"
|
||||
MISTRAL_AGENTS_ENDPOINT="https://api.mistral.ai/v1/agents"
|
||||
MISTRAL_FILES_ENDPOINT="https://api.mistral.ai/v1/files"
|
||||
MISTRAL_MODELS_ENDPOINT="https://api.mistral.ai/v1/models"
|
||||
ENV_FILE="$ROOT_DIR/tmp/imagegen/.env.mistral"
|
||||
AGENT_CACHE_FILE="$ROOT_DIR/tmp/imagegen/.mistral-image-agent.id"
|
||||
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "$ENV_FILE"
|
||||
fi
|
||||
|
||||
MODEL="${MISTRAL_IMAGE_MODEL:-mistral-medium-2505}"
|
||||
AGENT_ID="${MISTRAL_IMAGE_AGENT_ID:-}"
|
||||
|
||||
mkdir -p "$OUTPUT_DIR" "$(dirname "$REPORT_FILE")"
|
||||
|
||||
if [[ "${1-}" == "--list-models" ]]; then
|
||||
if [[ -z "${MISTRAL_API_KEY:-}" ]]; then
|
||||
echo "MISTRAL_API_KEY is required."
|
||||
exit 1
|
||||
fi
|
||||
curl -sS "$MISTRAL_MODELS_ENDPOINT" \
|
||||
-H "Authorization: Bearer $MISTRAL_API_KEY" \
|
||||
-H "Content-Type: application/json" | jq .
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "${1-}" == "--reset-agent" ]]; then
|
||||
rm -f "$AGENT_CACHE_FILE"
|
||||
shift || true
|
||||
BATCH_FILE="${1:-$ROOT_DIR/tmp/imagegen/mistral/carnet-labo-assets-v2.jsonl}"
|
||||
fi
|
||||
|
||||
if [[ -z "${MISTRAL_API_KEY:-}" ]]; then
|
||||
echo "MISTRAL_API_KEY is required."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$BATCH_FILE" ]]; then
|
||||
echo "Missing batch file: $BATCH_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "jq is required."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
echo "curl is required."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "# Asset Run Report ($TODAY)" > "$REPORT_FILE"
|
||||
echo >> "$REPORT_FILE"
|
||||
echo "- Batch: \`$BATCH_FILE\`" >> "$REPORT_FILE"
|
||||
echo "- Output: \`$OUTPUT_DIR\`" >> "$REPORT_FILE"
|
||||
echo "- Model: \`$MODEL\`" >> "$REPORT_FILE"
|
||||
echo >> "$REPORT_FILE"
|
||||
echo "| Asset | Provider | Status | Note |" >> "$REPORT_FILE"
|
||||
echo "|---|---|---|---|" >> "$REPORT_FILE"
|
||||
|
||||
log_report() {
|
||||
local asset="$1"
|
||||
local provider="$2"
|
||||
local status="$3"
|
||||
local note="$4"
|
||||
echo "| $asset | $provider | $status | $note |" >> "$REPORT_FILE"
|
||||
}
|
||||
|
||||
post_json() {
|
||||
local url="$1"
|
||||
local payload="$2"
|
||||
local out_file="$3"
|
||||
local http_code
|
||||
|
||||
http_code="$(curl -sS -o "$out_file" -w "%{http_code}" -X POST "$url" \
|
||||
-H "Authorization: Bearer $MISTRAL_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$payload")"
|
||||
|
||||
echo "$http_code"
|
||||
}
|
||||
|
||||
extract_file_ids_from_conversation() {
|
||||
local json_file="$1"
|
||||
jq -r '
|
||||
.outputs // [] |
|
||||
map(select(.type == "message.output")) |
|
||||
.[].content // [] |
|
||||
map(select(.type == "tool_file" and .file_id != null and .file_id != "")) |
|
||||
.[].file_id
|
||||
' "$json_file" 2>/dev/null | tr -d "\\r" | sed "/^$/d"
|
||||
}
|
||||
|
||||
extract_first_image_url() {
|
||||
local json_file="$1"
|
||||
jq -r '.data[0].url // empty' "$json_file" 2>/dev/null
|
||||
}
|
||||
|
||||
ensure_agent() {
|
||||
if [[ -n "$AGENT_ID" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ -f "$AGENT_CACHE_FILE" ]]; then
|
||||
local cached
|
||||
cached="$(tr -d '[:space:]' < "$AGENT_CACHE_FILE")"
|
||||
if [[ -n "$cached" ]]; then
|
||||
AGENT_ID="$cached"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
local payload tmpfile http_code
|
||||
payload="$(jq -nc \
|
||||
--arg model "$MODEL" \
|
||||
'{model:$model,name:"Image Generation Agent",description:"Image generation fallback agent",instructions:"Use image_generation for image requests.",tools:[{type:"image_generation"}]}')"
|
||||
tmpfile="$(mktemp)"
|
||||
http_code="$(post_json "$MISTRAL_AGENTS_ENDPOINT" "$payload" "$tmpfile")"
|
||||
|
||||
if [[ "$http_code" != 2* ]] || jq -e '.error' "$tmpfile" >/dev/null 2>&1; then
|
||||
echo "Unable to create Mistral fallback agent."
|
||||
cat "$tmpfile"
|
||||
rm -f "$tmpfile"
|
||||
return 1
|
||||
fi
|
||||
|
||||
AGENT_ID="$(jq -r '.id // empty' "$tmpfile")"
|
||||
rm -f "$tmpfile"
|
||||
if [[ -z "$AGENT_ID" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$AGENT_ID" > "$AGENT_CACHE_FILE"
|
||||
export MISTRAL_IMAGE_AGENT_ID="$AGENT_ID"
|
||||
}
|
||||
|
||||
copy_from_local_fallback() {
|
||||
local out="$1"
|
||||
local target="$2"
|
||||
local cache1="$ROOT_DIR/output/imagegen/openai/carnet-labo-assets-v1/$out"
|
||||
local cache2="$ROOT_DIR/public/assets/da/openai/$out"
|
||||
|
||||
if [[ -f "$cache1" ]]; then
|
||||
cp "$cache1" "$target"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ -f "$cache2" ]]; then
|
||||
cp "$cache2" "$target"
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
download_mistral_file_id() {
|
||||
local file_id="$1"
|
||||
local target="$2"
|
||||
|
||||
curl -L -sS "$MISTRAL_FILES_ENDPOINT/$file_id/content" \
|
||||
-H "Authorization: Bearer $MISTRAL_API_KEY" \
|
||||
-o "$target"
|
||||
}
|
||||
|
||||
generate_with_images_api() {
|
||||
local prompt="$1"
|
||||
local size="$2"
|
||||
local out_tmp="$3"
|
||||
local payload
|
||||
|
||||
payload="$(jq -nc --arg model "$MODEL" --arg prompt "$prompt" --arg size "$size" '{model:$model,prompt:$prompt,n:1,size:$size}')"
|
||||
post_json "$MISTRAL_IMAGES_ENDPOINT" "$payload" "$out_tmp"
|
||||
}
|
||||
|
||||
generate_with_agent_api() {
|
||||
local prompt="$1"
|
||||
local out_tmp="$2"
|
||||
local payload
|
||||
|
||||
ensure_agent || return 1
|
||||
payload="$(jq -nc --arg agent_id "$AGENT_ID" --arg prompt "$prompt" '{agent_id:$agent_id,inputs:$prompt,stream:false}')"
|
||||
post_json "$MISTRAL_CONVERSATIONS_ENDPOINT" "$payload" "$out_tmp"
|
||||
}
|
||||
|
||||
echo "Backend: Mistral image generation"
|
||||
echo "Model: $MODEL"
|
||||
echo "Batch: $BATCH_FILE"
|
||||
echo "Output: $OUTPUT_DIR"
|
||||
echo "Report: $REPORT_FILE"
|
||||
echo
|
||||
|
||||
tmpfile="$(mktemp)"
|
||||
trap 'rm -f "$tmpfile"' EXIT
|
||||
|
||||
success_count=0
|
||||
fallback_count=0
|
||||
error_count=0
|
||||
|
||||
while IFS= read -r line; do
|
||||
[[ -z "$line" ]] && continue
|
||||
|
||||
out="$(echo "$line" | jq -r '.out // ""')"
|
||||
prompt="$(echo "$line" | jq -r '.prompt // ""')"
|
||||
size="$(echo "$line" | jq -r '.size // "1024x1024"')"
|
||||
guardrails="$(echo "$line" | jq -r '.style_guardrails // ""')"
|
||||
target="$OUTPUT_DIR/$out"
|
||||
|
||||
if [[ -z "$out" || -z "$prompt" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
case "$size" in
|
||||
256x256|512x512|1024x1024) ;;
|
||||
*) size="1024x1024" ;;
|
||||
esac
|
||||
|
||||
if [[ -n "$guardrails" ]]; then
|
||||
prompt="$prompt Constraints: $guardrails"
|
||||
fi
|
||||
|
||||
if [[ -f "$target" ]]; then
|
||||
log_report "$out" "cache-output" "ok" "already exists"
|
||||
((success_count+=1))
|
||||
continue
|
||||
fi
|
||||
|
||||
provider=""
|
||||
status="failed"
|
||||
note=""
|
||||
|
||||
# Stage 1: images/generations with exponential retry on 503
|
||||
for attempt in 1 2 3 4; do
|
||||
http_code="$(generate_with_images_api "$prompt" "$size" "$tmpfile")"
|
||||
|
||||
if [[ "$http_code" == "503" ]]; then
|
||||
sleep $((2 ** (attempt - 1)))
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ "$http_code" == 2* ]] && ! jq -e '.error' "$tmpfile" >/dev/null 2>&1; then
|
||||
url="$(extract_first_image_url "$tmpfile")"
|
||||
if [[ -n "$url" ]]; then
|
||||
if curl -L -sS "$url" -o "$target"; then
|
||||
provider="mistral-images"
|
||||
status="ok"
|
||||
note="generated via /v1/images/generations"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
break
|
||||
done
|
||||
|
||||
# Stage 2: fallback to agent/conversations
|
||||
if [[ "$status" != "ok" ]]; then
|
||||
for attempt in 1 2 3 4; do
|
||||
http_code="$(generate_with_agent_api "$prompt" "$tmpfile" || echo "000")"
|
||||
if [[ "$http_code" == "503" ]]; then
|
||||
sleep $((2 ** (attempt - 1)))
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ "$http_code" == 2* ]] && ! jq -e '.error' "$tmpfile" >/dev/null 2>&1; then
|
||||
file_id="$(extract_file_ids_from_conversation "$tmpfile" | head -n 1)"
|
||||
if [[ -n "$file_id" ]]; then
|
||||
if download_mistral_file_id "$file_id" "$target"; then
|
||||
provider="mistral-agent"
|
||||
status="ok"
|
||||
note="generated via /v1/conversations fallback"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
break
|
||||
done
|
||||
fi
|
||||
|
||||
# Stage 3: fallback to local cache
|
||||
if [[ "$status" != "ok" ]]; then
|
||||
if copy_from_local_fallback "$out" "$target"; then
|
||||
provider="local-cache"
|
||||
status="ok"
|
||||
note="fallback from openai/public cache"
|
||||
((fallback_count+=1))
|
||||
else
|
||||
provider="none"
|
||||
status="failed"
|
||||
note="no provider available"
|
||||
((error_count+=1))
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$status" == "ok" ]]; then
|
||||
((success_count+=1))
|
||||
fi
|
||||
log_report "$out" "$provider" "$status" "$note"
|
||||
echo "$status: $out ($provider)"
|
||||
done < "$BATCH_FILE"
|
||||
|
||||
echo >> "$REPORT_FILE"
|
||||
echo "- Success: $success_count" >> "$REPORT_FILE"
|
||||
echo "- Local-cache fallback: $fallback_count" >> "$REPORT_FILE"
|
||||
echo "- Errors: $error_count" >> "$REPORT_FILE"
|
||||
|
||||
if (( error_count > 0 )); then
|
||||
echo "Completed with errors. See $REPORT_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Done. Report: $REPORT_FILE"
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
"$SCRIPT_DIR/gen-carnet-labo-assets-mistral.sh" "${SCRIPT_DIR}/../tmp/imagegen/mistral/carnet-labo-assets-v2.jsonl"
|
||||
Reference in New Issue
Block a user