Skip to content

feat: color-palette-generator - agentic image color extraction with K…#34

Open
A-VISHAL wants to merge 21 commits into
devfrom
feat/color-palette-generator
Open

feat: color-palette-generator - agentic image color extraction with K…#34
A-VISHAL wants to merge 21 commits into
devfrom
feat/color-palette-generator

Conversation

@A-VISHAL
Copy link
Copy Markdown
Collaborator

@A-VISHAL A-VISHAL commented May 6, 2026

…Means + LLM refinement

Oxtools Submission

Project name: [Your project name]
Contributor: [@your-github-handle]
Demo: [Link to Loom or YouTube recording — required]


What does this tool do?

Write 2–3 sentences describing the tool, the problem it solves, and how it uses the Oxlo API.


Submission checklist

Check every box before requesting a review. Unchecked items will result in the PR being sent back.

Structure

  • My project is in its own directory under projects/[my-project-name]/
  • I have not placed any files directly in the repository root

Required files

  • Dockerfile is present and docker build . succeeds
  • docker-compose.yml is present and docker compose up starts the app
  • oxlo-manifest.json is present and all fields are filled in
  • .env.example lists every environment variable the project needs (with empty values)
  • README.md is present with setup instructions a reviewer can follow exactly

Security

  • No API keys, private keys, or secrets are hardcoded anywhere in the codebase
  • My actual .env file is not included in this PR
  • I have verified my diff with git grep -i "api_key" and found no leaks

Oxlo API

  • The tool makes at least one functional call to the Oxlo API
  • The API key is read from the OXLO_API_KEY environment variable

For maintainers

  • Security scan passed — no secrets in diff
  • docker build . succeeded locally
  • docker compose up ran successfully and app is reachable
  • Oxlo API integration verified
  • Approved for merge

Copilot AI review requested due to automatic review settings May 6, 2026 10:38
@vercel
Copy link
Copy Markdown

vercel Bot commented May 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
oxtools Ready Ready Preview, Comment May 20, 2026 1:09pm

Request Review

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new tier-2 “Color Palette Generator” tool that extracts dominant image colors (KMeans) and optionally refines them via an Oxlo-backed LLM pipeline, plus frontend updates to support image upload, long-running execution, and richer palette/image previews.

Changes:

  • Introduces a new Python tool (image-palette-extractor) with KMeans extraction, LangGraph orchestration, and LLM refinement.
  • Updates the frontend tool definition and tool execution hook for tier-2 image workflows (upload, compression, longer timeouts).
  • Adds/updates result rendering to show early extracted colors and an interactive image hover/pixel-inspection viewer.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
services/python-tools/tools/image-palette-extractor/tool.py Tool entrypoint + streaming output markers + fallback behavior
services/python-tools/tools/image-palette-extractor/requirements.txt Tool-specific Python dependencies
services/python-tools/tools/image-palette-extractor/pipeline.py LangGraph state machine for validate→extract→refine→format
services/python-tools/tools/image-palette-extractor/llm_refiner.py LLM call + JSON extraction + WCAG “compliance” wrapper
services/python-tools/tools/image-palette-extractor/config.py Oxlo ChatOpenAI client config + refinement system prompt
services/python-tools/tools/image-palette-extractor/color_extractor.py Base64 image decoding + KMeans clustering + preview/pixel-map helper
app/src/lib/tools/color-palette.ts Switches tool to tier2 + image input + model metadata
app/src/hooks/use-tool-execution.ts Adds per-tool timeouts and abort handling
app/src/components/result-viewer.tsx Parses new output markers + renders early extracted colors + uses new viewer
app/src/components/color-palette-viewer.tsx New interactive image/palette viewer (canvas hover inspection)
app/src/app/tools/[toolId]/page.tsx Client-side image validation/compression + tier2 execution wiring

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

Comment on lines +118 to +126
# Re-encode resized image to data URI (jpeg to reduce size)
buffer = io.BytesIO()
image.save(buffer, format="JPEG", quality=85)
buffer.seek(0)
resized_b64 = buffer.getvalue()
from base64 import b64encode

data_uri = f"{header};base64,{b64encode(resized_b64).decode('utf-8')}"

Comment on lines +98 to +116
image_array = np.array(image)

# Determine sampling step to keep pixel count <= max_pixels
total = width * height
pixels = []
if max_pixels and max_pixels > 0:
if total <= max_pixels:
step = 1
else:
# sample roughly uniformly using a square step
step = int(max(1, (total / max_pixels) ** 0.5))

for y in range(0, height, step):
for x in range(0, width, step):
rgb = image_array[y, x]
hex_color = self._rgb_to_hex(tuple(rgb))
pixels.append({"x": int(x), "y": int(y), "color": hex_color})
else:
step = 0
Comment on lines +160 to +167
"roles": {
"primary": extracted_colors[0] if extracted_colors else "#000000",
"secondary": extracted_colors[1] if len(extracted_colors) > 1 else extracted_colors[0],
"accent": extracted_colors[2] if len(extracted_colors) > 2 else extracted_colors[0],
"background": "#ffffff",
"surface": "#f5f5f5",
"text": "#333333",
"muted": "#999999",
Comment on lines +156 to +168
# CREATE FALLBACK PALETTE from extracted colors
result = {
"success": True,
"palette": {color: color for color in extracted_colors[:min(num_colors, len(extracted_colors))]},
"roles": {
"primary": extracted_colors[0] if extracted_colors else "#000000",
"secondary": extracted_colors[1] if len(extracted_colors) > 1 else extracted_colors[0],
"accent": extracted_colors[2] if len(extracted_colors) > 2 else extracted_colors[0],
"background": "#ffffff",
"surface": "#f5f5f5",
"text": "#333333",
"muted": "#999999",
},
Comment on lines +19 to +26
def _load_module_from_file(filename: str):
"""Load a module explicitly from a file path to avoid collisions."""
module_name = filename.replace('.py', '')

# Clear from sys.modules cache to force fresh load
if module_name in sys.modules:
del sys.modules[module_name]

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.

Please look into this is as well, it can cause issues further, so have config.py file for this and have them imported.

Comment thread services/python-tools/tools/image-palette-extractor/llm_refiner.py
Comment on lines +134 to +143
if (err instanceof DOMException && err.name === "AbortError") {
// Request was aborted (either by timeout or user action)
if (!error) { // Only set timeout error if not already set
setError({
message: "Request was cancelled. Please try again.",
code: "aborted"
});
}
setResult("");
return;
Comment thread app/src/app/tools/[toolId]/page.tsx Outdated
Comment on lines +84 to +108
@@ -71,6 +105,7 @@ function ToolPageContent({ toolId }: { toolId: string }) {
const { canExecute, getToolUsage, trackExecution, redirectToUpgrade } = useAuth();
const toolUsage = getToolUsage(tool.id);
const [showUpgradeDialog, setShowUpgradeDialog] = useState(false);
const [hasExecuted, setHasExecuted] = useState(false);
Arunmadhavan28 and others added 2 commits May 11, 2026 10:49
Resolved conflicts in result-viewer.tsx by accepting HEAD version which includes:
- Color palette extraction and display features
- Pipeline logs viewer
- Extracted colors preview with copy functionality

Note: Incoming branch's Edit/Compare tabs and fullscreen editing features
should be integrated in a future PR for full feature parity.
Comment thread app/src/components/usage-counter.tsx
Comment on lines +19 to +26
def _load_module_from_file(filename: str):
"""Load a module explicitly from a file path to avoid collisions."""
module_name = filename.replace('.py', '')

# Clear from sys.modules cache to force fresh load
if module_name in sys.modules:
del sys.modules[module_name]

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.

Please look into this is as well, it can cause issues further, so have config.py file for this and have them imported.

@A-VISHAL A-VISHAL requested a review from beekay2706 as a code owner May 20, 2026 09:15
Comment thread app/next.config.ts
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.

There is still some extension related codes, please remove the extension related codes and files.

Comment thread app/next.config.ts Outdated
"chrome-extension://poaainbnlkonlkjiiemhfoflbkobamec"
]
},
allowedDevOrigins: ["chrome-extension://poaainbnlkonlkjiiemhfoflbkobamec"],
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.

Extension related codes.

Comment thread app/next-env.d.ts Outdated
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/dev/types/routes.d.ts";
import "./.next/types/routes.d.ts";
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.

Can you check on this? Why this was edited. Because this files were mentioned not to be edited. So once can you please look into this why this was edited.

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.

4 participants