Skip to content

feat: multi-pass AI research with draft approval and hero image generation#173

Merged
fatherlinux merged 1 commit into
masterfrom
feature/issue-102-99-multipass-research-hero-image
Apr 3, 2026
Merged

feat: multi-pass AI research with draft approval and hero image generation#173
fatherlinux merged 1 commit into
masterfrom
feature/issue-102-99-multipass-research-hero-image

Conversation

@fatherlinux
Copy link
Copy Markdown
Member

Summary

  • Replace single-pass Gemini research with two-pass workflow: Pass 1 (metadata + brief description) and Pass 2 (historical description) with URL-first grounding and Google Search
  • Add admin research context textarea for guiding AI research
  • Draft approval modal with per-field accept/reject toggles and editable values
  • Auto-generate 1800s-style hero images via Gemini image model concurrently after research completes
  • Robust JSON parser that handles truncated Gemini responses (token limit mid-array)
  • New research_context database column with migration
  • Three new API endpoints: /ai/research-v2, /ai/generate-hero-image, /ai/accept-hero-image

Closes #102
Closes #99

Test plan

  • Navigate to a sparse POI, enter research context, click "Research with AI"
  • Verify draft modal appears with Pass 1 + Pass 2 results and hero image auto-generating
  • Verify Accept button disabled until image generation completes
  • Accept fields, verify they populate in edit form
  • Test Regenerate and Remove on hero image
  • Verify Save Changes button disabled during research
  • Test prompt editor pencil icons on Brief/Historical Description labels
  • 222/224 tests pass (2 pre-existing flaky failures)

🤖 Generated with Claude Code

@fatherlinux fatherlinux force-pushed the feature/issue-102-99-multipass-research-hero-image branch from 62038b2 to 5bedba7 Compare April 3, 2026 01:11
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a multi-pass AI research system and hero image generation using Gemini. Key additions include a new research-v2 endpoint with draft approval, image generation capabilities, and a jitter utility for scheduled jobs to prevent bot detection. Feedback focuses on improving the robustness of file extension handling for generated images, refactoring duplicated API key retrieval logic, and enhancing security by moving API keys from URL parameters to request headers while adding necessary fetch timeouts.

Comment thread backend/routes/admin.js
}

try {
const imageBuffer = Buffer.from(imageData, 'base64');
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for determining the file extension based on the MIME type is incomplete. Gemini models can return images in image/webp format, which would currently default to a .png extension. This can lead to file type mismatches in storage or when serving the asset.

Suggested change
const imageBuffer = Buffer.from(imageData, 'base64');
const extension = mimeType === 'image/jpeg' ? 'jpg' : (mimeType === 'image/webp' ? 'webp' : 'png');

Comment on lines +634 to +643
let apiKey = process.env.GEMINI_API_KEY;
if (!apiKey) {
const apiKeyQuery = await pool.query(
"SELECT value FROM admin_settings WHERE key = 'gemini_api_key'"
);
if (!apiKeyQuery.rows.length || !apiKeyQuery.rows[0].value) {
throw new Error('Gemini API key not configured.');
}
apiKey = apiKeyQuery.rows[0].value;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for retrieving the Gemini API key from environment variables or the database is duplicated here. It is already implemented in the createGeminiClient function. Consider refactoring this into a shared helper function to improve maintainability and ensure consistency across the service.


// Use REST API directly for image generation (SDK may not support image output modality)
// Fix: use x-goog-api-key header instead of URL query param to avoid key leakage in logs (PR #173 review)
const modelName = GEMINI_IMAGE_MODEL;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

Passing the API key as a query parameter in the URL is less secure than using a request header, as URLs are frequently logged by proxies and servers. Additionally, the fetch call lacks a timeout, which could cause the request to hang indefinitely if the API is unresponsive.

  const url = `https://generativelanguage.googleapis.com/v1beta/models/${modelName}:generateContent`;

  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), 60000); // 60s timeout

  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-goog-api-key': apiKey
    },
    signal: controller.signal,

@fatherlinux fatherlinux force-pushed the feature/issue-102-99-multipass-research-hero-image branch 2 times, most recently from 7c6f2f4 to c3e0b9a Compare April 3, 2026 01:43
…ation (#102, #99)

Replace single-pass Gemini research with two-pass workflow (Pass 1: metadata + brief
description, Pass 2: historical description) with URL-first grounding, admin context
input, and draft approval step. Add 1800s-style hero image auto-generation via Gemini
image model that runs concurrently after research completes. Includes robust JSON parser
for truncated Gemini responses and editable draft fields with per-field accept/reject.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@fatherlinux fatherlinux force-pushed the feature/issue-102-99-multipass-research-hero-image branch from c3e0b9a to 0fb3191 Compare April 3, 2026 02:05
@fatherlinux fatherlinux merged commit a892195 into master Apr 3, 2026
4 checks passed
@fatherlinux fatherlinux deleted the feature/issue-102-99-multipass-research-hero-image branch April 3, 2026 02:15
fatherlinux added a commit that referenced this pull request Apr 4, 2026
Documents the withJitter() anti-bot detection feature that was
implemented and merged in PR #173 (commit 0fb3191). Spec written
after implementation to maintain documentation completeness per
constitution requirements.

Feature adds random 1-60 second delays to cron-scheduled jobs
(news collection, trail status, moderation sweep, backups) to
avoid predictable timing patterns that trigger bot detection.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: AI-Powered POI Research Workflow [Feature]: AI-Generated 1800s Hero Images for POIs

1 participant