diff --git a/.agents/workflows/resolve-issues.md b/.agents/workflows/resolve-issues.md index aee2d41656..c5a07b7b04 100644 --- a/.agents/workflows/resolve-issues.md +++ b/.agents/workflows/resolve-issues.md @@ -1,12 +1,12 @@ --- -description: Fetch all open GitHub issues, analyze bugs, resolve what's possible, triage the rest, then commit and release +description: Fetch all open GitHub issues, analyze bugs, resolve what's possible, triage the rest, wait for user validation, then commit and release --- # /resolve-issues — Automated Issue Resolution Workflow ## Overview -This workflow fetches all open issues from the project's GitHub repository, classifies them, analyzes bugs, resolves what can be fixed, triages issues with insufficient information, and generates a release with all fixes. +This workflow fetches all open issues from the project's GitHub repository, classifies them, analyzes bugs, resolves what can be fixed, and triages issues with insufficient information. **It does NOT commit or release automatically** — it presents a report and waits for user validation before proceeding. ## Steps @@ -64,32 +64,39 @@ Proceed with resolution: 2. **Root Cause** — Identify the root cause by reading the relevant source files 3. **Implement Fix** — Apply the fix following existing code patterns and conventions 4. **Test** — Build the project and run tests to verify the fix -5. **Commit** — Commit with message format: `fix: (#)` +5. **DO NOT commit yet** — Leave changes staged but uncommitted -### 5. Commit All Fixes +### 5. Generate Report & Wait for Validation -After processing all issues: +Present a summary report to the user via `notify_user` with `BlockedOnUser: true`: -- Ensure all fixes are committed with proper issue references +| Issue | Title | Status | Action | +| ----- | ----- | ------------- | ----------------------------- | +| #N | Title | ✅ Ready | Files changed (not committed) | +| #N | Title | ❓ Needs Info | Triage comment posted | +| #N | Title | ⏭️ Skipped | Feature request / not a bug | + +> **⚠️ IMPORTANT**: Do NOT commit, close issues, or generate releases at this step. +> Wait for the user to review the changes and respond with **OK** before proceeding. + +- If the user says **OK** or approves → Proceed to step 6 +- If the user requests changes → Apply the requested adjustments first, then present the report again +- If the user rejects → Revert the changes and stop + +### 6. Commit All Fixes (only after user approval) + +After the user validates: + +- Commit each fix individually with message format: `fix: (#)` - Each fix should be its own commit for clean git history -### 6. Close Resolved Issues +### 7. Close Resolved Issues For each successfully fixed issue: // turbo - Close with a comment: `gh issue close --repo / --comment "Fixed in . The fix will be included in the next release."` -### 7. Generate Report - -Present a summary report to the user via `notify_user`: - -| Issue | Title | Status | Action | -| ----- | ----- | ------------- | --------------------------- | -| #N | Title | ✅ Fixed | Commit hash | -| #N | Title | ❓ Needs Info | Triage comment posted | -| #N | Title | ⏭️ Skipped | Feature request / not a bug | - ### 8. Update Docs & Release If any fixes were committed: diff --git a/open-sse/config/registryUtils.ts b/open-sse/config/registryUtils.ts index cd20cc6f46..869d04dcc0 100644 --- a/open-sse/config/registryUtils.ts +++ b/open-sse/config/registryUtils.ts @@ -53,9 +53,10 @@ export function parseModelFromRegistry

( */ export function getAllModelsFromRegistry

( registry: Record, - extra?: (providerId: string, config: P) => Record -): Array<{ id: string; name: string; provider: string } & Record> { - const models: Array<{ id: string; name: string; provider: string } & Record> = []; + extra?: (providerId: string, config: P) => Record +): Array<{ id: string; name: string; provider: string } & Record> { + const models: Array<{ id: string; name: string; provider: string } & Record> = + []; for (const [providerId, config] of Object.entries(registry)) { const extraFields = extra ? extra(providerId, config) : {}; diff --git a/src/app/(dashboard)/dashboard/media/MediaPageClient.tsx b/src/app/(dashboard)/dashboard/media/MediaPageClient.tsx new file mode 100644 index 0000000000..813293f5b3 --- /dev/null +++ b/src/app/(dashboard)/dashboard/media/MediaPageClient.tsx @@ -0,0 +1,261 @@ +"use client"; + +import { useState } from "react"; +import { useTranslations } from "next-intl"; + +type Modality = "image" | "video" | "music"; +type GenerationResult = { + type: Modality; + data: any; + timestamp: number; +}; + +const MODALITY_CONFIG: Record< + Modality, + { icon: string; endpoint: string; label: string; placeholder: string; color: string } +> = { + image: { + icon: "image", + endpoint: "/api/v1/images/generations", + label: "Image Generation", + placeholder: "A serene landscape with mountains at sunset...", + color: "from-purple-500 to-pink-500", + }, + video: { + icon: "videocam", + endpoint: "/api/v1/videos/generations", + label: "Video Generation", + placeholder: "A timelapse of a flower blooming...", + color: "from-blue-500 to-cyan-500", + }, + music: { + icon: "music_note", + endpoint: "/api/v1/music/generations", + label: "Music Generation", + placeholder: "Upbeat electronic music with synth pads...", + color: "from-orange-500 to-yellow-500", + }, +}; + +export default function MediaPageClient() { + const t = useTranslations("media"); + const [activeTab, setActiveTab] = useState("image"); + const [prompt, setPrompt] = useState(""); + const [model, setModel] = useState(""); + const [models, setModels] = useState([]); + const [loading, setLoading] = useState(false); + const [loadingModels, setLoadingModels] = useState(false); + const [result, setResult] = useState(null); + const [error, setError] = useState(null); + + // Fetch available models for each modality + const fetchModels = async (modality: Modality) => { + setLoadingModels(true); + try { + const res = await fetch(MODALITY_CONFIG[modality].endpoint); + if (res.ok) { + const data = await res.json(); + const modelList = data.data || []; + setModels(modelList); + if (modelList.length > 0) setModel(modelList[0].id); + } + } catch { + setModels([]); + } + setLoadingModels(false); + }; + + const switchTab = (tab: Modality) => { + setActiveTab(tab); + setPrompt(""); + setResult(null); + setError(null); + fetchModels(tab); + }; + + const handleGenerate = async () => { + if (!prompt.trim()) return; + setLoading(true); + setError(null); + setResult(null); + + try { + const config = MODALITY_CONFIG[activeTab]; + const res = await fetch(config.endpoint, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + model: model || undefined, + prompt: prompt.trim(), + ...(activeTab === "image" ? { size: "1024x1024", n: 1 } : {}), + }), + }); + + if (!res.ok) { + const errData = await res.json().catch(() => ({})); + throw new Error(errData?.error?.message || `Generation failed (${res.status})`); + } + + const data = await res.json(); + setResult({ type: activeTab, data, timestamp: Date.now() }); + } catch (err: any) { + setError(err.message || "Generation failed"); + } + setLoading(false); + }; + + // Load models on first render + useState(() => { + fetchModels("image"); + }); + + const config = MODALITY_CONFIG[activeTab]; + + return ( +

+ {/* Header */} +
+

{t("title")}

+

{t("subtitle")}

+
+ + {/* Modality Tabs */} +
+ {(Object.keys(MODALITY_CONFIG) as Modality[]).map((key) => { + const cfg = MODALITY_CONFIG[key]; + const isActive = key === activeTab; + return ( + + ); + })} +
+ + {/* Generation Form */} +
+ {/* Model selector */} +
+ + {loadingModels ? ( +
+ progress_activity + {t("loadingModels")} +
+ ) : models.length > 0 ? ( + + ) : ( +

{t("noModels")}

+ )} +
+ + {/* Prompt */} +
+ +