Skip to content

fix: image generation with nearai models - #1819

Merged
think-in-universe merged 24 commits into
stagingfrom
fix/image-gen
Apr 14, 2026
Merged

think-in-universe merged 24 commits into
stagingfrom
fix/image-gen

Conversation

@hanakannzashi

Copy link
Copy Markdown
Contributor

Summary

  • Fix image generation against NearAI STG by avoiding /v1/v1/... URL construction and switching the fallback image model to black-forest-labs/FLUX.2-klein-4B.
  • Fix generated-image handling end to end: detect image sentinels reliably, emit image_generated events, and avoid feeding raw base64/data URLs back into the model.
  • Persist generated-image payloads in tool-call history and rebuild them into generated_images so images survive page refresh and render as standalone UI blocks.
  • Fix generated image rendering in the web UI so image cards do not collapse when later assistant text is added.

Change Type

  • Bug fix
  • New feature
  • Refactor
  • Documentation
  • CI/Infrastructure
  • Security
  • Dependencies

Linked Issue

Fixes #1810

Validation

  • cargo fmt --all -- --check
  • cargo clippy --all --benches --tests --examples --all-features -- -D warnings
  • cargo build
  • Relevant tests pass:
    • cargo test -q test_build_turns_collects_generated_images_from_persisted_tool_results --lib
    • cargo test -q test_rebuild_chat_messages_summarizes_image_generated_result --lib
    • cargo test -q test_image_generation_summary_tool_message_omits_data_url --lib
    • cargo test -q test_parse_image_generated_sentinel_accepts_stringified_json --lib
    • cargo test -q parses_double_stringified_sentinel --lib
  • cargo test --features integration if database-backed or integration behavior changed
  • Manual testing:
    • verified successful image generation against NearAI STG
    • verified assistant text no longer includes embedded <img src="data:...">
    • verified generated images are returned as standalone UI blocks
    • verified generated images persist after page refresh
  • If a coding agent was used and supports it, review-pr or pr-shepherd --fix was run before requesting review

Security Impact

None. No auth, secrets, sandbox, or permission model changes. This PR does stop raw generated-image base64 from being echoed back into model-visible tool results.

Database Impact

None. No schema or migration changes. This does change persisted tool_calls.result content for generated-image tool results so history can reconstruct images, but it remains within existing JSON/text storage and is compatible with both PostgreSQL and libSQL.

Blast Radius

Touches image tool execution, agent tool-result handling, persisted tool-call history, and web chat/history rendering. Regressions would most likely affect generated-image display, tool-result previews, or refresh/history reconstruction for image turns.

Review Follow-Through

  • Current image-model selection still relies on inferring from the active chat model name plus a hardcoded fallback.
  • We should add an explicit manually configurable image model setting, but that is intentionally out of scope for this PR.

Review track: B

@github-actions github-actions Bot added scope: agent Agent core (agent loop, router, scheduler) scope: channel/web Web gateway channel scope: tool/builtin Built-in tools size: XL 500+ changed lines risk: medium Business logic, config, or moderate-risk modules contributor: new First-time contributor labels Mar 31, 2026

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

Copy link
Copy Markdown
Contributor

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 implements a comprehensive system for handling, persisting, and displaying generated images within the agentic loop and web interface. Key changes include the introduction of a GeneratedImageSentinel to manage image metadata, logic to omit large base64 data URLs from LLM context while preserving them for frontend reconstruction, and automatic media type inference (JPEG, PNG, GIF, WebP) for generated assets. The web frontend was updated to cache generated images per thread and handle history reconstruction more effectively. Review feedback highlights opportunities to optimize frontend memory usage, refine scrolling behavior during history loads, and ensure consistent state synchronization during pagination.

Comment thread crates/ironclaw_gateway/static/app.js
Comment thread src/channels/web/static/app.js Outdated
Comment thread crates/ironclaw_gateway/static/app.js
Comment thread src/channels/web/static/app.js Outdated

@zmanian zmanian left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Review -- COMMENT (leaning APPROVE)

Well-structured fix for image persistence and LLM context pollution. Good test coverage (7 new tests). URL construction fix is correct. Both DB backends work without schema changes.

Medium (worth addressing)

  • Unbounded image storage in DB: tool_result_content_for_persistence stores full base64 data URLs (1-5 MB each) with no truncation, unlike other tool results (1000 char limit). Acknowledge the storage growth implication or add a size cap.
  • Duplicated endpoint_url(): Exact same logic in ImageGenerateTool and ImageEditTool. Extract to a shared helper.

Low (non-blocking)

  • Frontend generatedImagesByThread Map grows unbounded (cleared on page refresh)
  • DOM dedup compares full data URL strings (a Set of IDs would be cleaner)
  • normalize_embedded_json limit of 3 parse rounds is undocumented

@github-actions github-actions Bot added contributor: regular 2-5 merged PRs and removed contributor: new First-time contributor labels Apr 1, 2026
@hanakannzashi hanakannzashi changed the title Persist generated images in history and trim image tool output Fix image generation Apr 1, 2026
# Conflicts:
#	src/channels/web/handlers/chat.rs
#	src/lib.rs
zmanian
zmanian previously approved these changes Apr 3, 2026

@zmanian zmanian left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-review -- APPROVE

The three new commits (dce9e8cb, a02a87bc, 89b3a911) address all medium-priority items from the previous review and most low-priority ones:

Previously raised -- status

Item Status
Unbounded image storage in DB Fixed -- MAX_PERSISTED_IMAGE_SENTINEL_BYTES (8 MiB) cap with clear fallback message
Duplicated endpoint_url() Fixed -- extracted to image_api_endpoint_url() in src/tools/builtin/mod.rs
Frontend generatedImagesByThread unbounded Fixed -- LRU eviction with GENERATED_IMAGE_THREAD_CACHE_CAP = 20 / GENERATED_IMAGES_PER_THREAD_CAP = 8
Scroll-per-image during history load Fixed -- addGeneratedImage(url, path, false) + single scroll after loop
normalize_embedded_json limit undocumented Fixed -- comment explaining the 3-round cap
Pagination not calling rememberGeneratedImage Fixed -- older-history path now calls rememberGeneratedImage
Redundant renderGeneratedImagesForCurrentThread() Removed; replaced with a single container.scrollTop = container.scrollHeight
DOM dedup via full data URL string comparison Not addressed (low priority, acceptable for now)

New changes look correct

  • ironclaw_safety::SafetyLayer import migration in dispatcher.rs follows the project convention (CLAUDE.md: "new code should import from ironclaw_safety directly").
  • Module declaration ordering fix in lib.rs is cosmetic and correct.
  • The merge from staging resolved cleanly based on the diff.

One minor observation (non-blocking)

infer_generated_image_media_type decodes only the first 64 characters of base64 (not 64 bytes). Due to base64 encoding ratios, this yields ~48 decoded bytes, which is enough for all four magic-number checks (JPEG needs 3, PNG needs 8, GIF needs 6, WebP needs 12). Just noting this is fine as-is.

Good work addressing the feedback thoroughly.

# Conflicts:
#	src/agent/dispatcher.rs
@think-in-universe think-in-universe changed the title Fix image generation fix: image generation Apr 4, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Fixes image generation and rendering across the tool execution layer, persisted history reconstruction, SSE eventing, and the web UI—specifically addressing NearAI STG /v1/v1 endpoint construction issues and ensuring generated images persist and render correctly after refresh.

Changes:

  • Centralized image API endpoint URL construction to avoid duplicated /v1 and updated the default fallback image model.
  • Standardized “image_generated” sentinel parsing/handling end-to-end (agent ↔ persistence ↔ web history), including summarizing tool results to avoid feeding base64/data URLs back into the model.
  • Updated web UI history rendering and CSS so generated image cards render as stable standalone blocks and don’t collapse when additional assistant messages are appended.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/tools/builtin/mod.rs Adds shared helper to build image API endpoints without /v1 duplication.
src/tools/builtin/image_gen.rs Uses shared endpoint helper; infers media type from base64 prefix; emits data URLs with detected media type; adds tests.
src/tools/builtin/image_edit.rs Uses shared endpoint helper for edits/fallback; applies media-type inference; adds tests.
src/lib.rs Registers new internal generated_images module.
src/generated_images.rs Introduces GeneratedImageSentinel parser/normalizer to reliably detect sentinels across stringification layers.
src/channels/web/util.rs Collects generated images from persisted tool results; adds tool result preview that hides sentinel payloads.
src/channels/web/types.rs Extends TurnInfo with generated_images and defines GeneratedImageInfo for API responses.
src/channels/web/static/style.css Adjusts generated image card/layout styling to prevent collapse and keep sizing stable.
src/channels/web/static/app.js Renders generated image blocks from SSE + history; adds de-dupe/caching helpers and scroll behavior changes.
src/channels/web/server.rs Uses new preview + generated image collection when building history responses.
src/channels/web/handlers/chat.rs Same as server history path: uses preview + generated image collection for responses.
src/app.rs Updates fallback image model to black-forest-labs/FLUX.2-klein-4B.
src/agent/thread_ops.rs Persists full image sentinel payloads (with size cap) and summarizes sentinel tool results when rebuilding model context.
src/agent/dispatcher.rs Uses shared sentinel detection; emits image_generated; replaces tool-message content with a summary that omits base64/data URLs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/ironclaw_gateway/static/app.js
@think-in-universe think-in-universe changed the title fix: image generation fix: image generation with nearai models Apr 6, 2026
This was referenced Apr 16, 2026
theredspoon pushed a commit to theredspoon/ironclaw that referenced this pull request Jun 21, 2026
Co-authored-by: Robert Yan <46699230+think-in-universe@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: experienced 6-19 merged PRs risk: medium Business logic, config, or moderate-risk modules scope: agent Agent core (agent loop, router, scheduler) scope: channel/wasm WASM channel runtime scope: channel/web Web gateway channel scope: channel Channel infrastructure scope: tool/builtin Built-in tools size: XL 500+ changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Image generation failed

5 participants