fix: genera el video queues the prepared Studio form - #46
Conversation
PR Review — Loreframe StudioRisk: low Automated review from Findings
Changed files
CONTRIBUTING checklist
Posted by the repo PR review workflow. Re-runs on each push to the PR. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: Resume matcher captures topical requests
- isResumePreparedStudioVideoRequest now requires leftover text after the article-plus-video command to be empty, so topical and example phrasing fall through to maybeExampleTurn and isExplicitVideoGenerationRequest.
- ✅ Fixed: Resume copies incompatible current model
- The store-built resume action omits modelType so prepareVideo can pick a T2V model, and it reads savedPromptPerMode.video after leaving Video instead of the active mode prompt.
Or push these changes by commenting:
@cursor push f01673620c
Preview (f01673620c)
diff --git a/ui/src/features/agent/agentActions.ts b/ui/src/features/agent/agentActions.ts
--- a/ui/src/features/agent/agentActions.ts
+++ b/ui/src/features/agent/agentActions.ts
@@ -1697,14 +1697,21 @@
return EXPLICIT_VIDEO_REQUESTS.some(pattern => pattern.test(text))
}
-const RESUME_PREPARED_VIDEO_REQUESTS = [
- /\b(?:genera|generad|lanza|lanzad|encola|encolad|env[ií]a|enviad|start|queue|launch)\b[^.!?\n]{0,24}\b(?:el|la|este|esta|the)\s+(?:video|v[ií]deo|clip)\b/i,
-]
-
export function isResumePreparedStudioVideoRequest(request: string): boolean {
const text = request.trim()
if (!text || NEGATED_VIDEO_REQUEST.test(text)) return false
- return RESUME_PREPARED_VIDEO_REQUESTS.some(pattern => pattern.test(text))
+ const match = text.match(
+ /\b(?:genera|generad|lanza|lanzad|encola|encolad|env[ií]a|enviad|start|queue|launch)\b[^.!?\n]{0,24}\b(?:el|la|este|esta|the)\s+(?:video|v[ií]deo|clip)\b(.*)$/i,
+ )
+ if (!match) return false
+ const leftover = match[1]
+ .toLowerCase()
+ .normalize('NFD')
+ .replace(/[\u0300-\u036f]/g, '')
+ .replace(/[¿?¡!.,:;]/g, ' ')
+ .replace(/\b(?:por favor|please|ya|ahora|now)\b/g, ' ')
+ .replace(/\s+/g, '')
+ return leftover.length === 0
}
const EXPLICIT_IMAGE_REQUESTS = [
@@ -1858,7 +1865,10 @@
(action): action is AgentPrepareVideoAction => action.type === 'prepare_video',
)
const state = useStore.getState()
- const prompt = existing?.prompt.trim() || String(state.params.prompt || '').trim()
+ const studioPrompt = state.generationMode === 'video'
+ ? String(state.params.prompt || '').trim()
+ : String(state.savedPromptPerMode?.video || '').trim()
+ const prompt = existing?.prompt.trim() || studioPrompt
if (!prompt) {
return {
reply: 'Studio no tiene un prompt preparado. Dime la escena y la encolo.',
@@ -1868,10 +1878,9 @@
const prepare: AgentPrepareVideoAction = existing || {
type: 'prepare_video',
prompt,
- modelType: typeof state.params.model_type === 'string' ? state.params.model_type : undefined,
- durationSeconds: state.durationSeconds,
- resolutionPreset: state.resolutionPreset,
- aspectRatio: state.aspectRatio,
+ durationSeconds: state.generationMode === 'video' ? state.durationSeconds : undefined,
+ resolutionPreset: state.generationMode === 'video' ? state.resolutionPreset : undefined,
+ aspectRatio: state.generationMode === 'video' ? state.aspectRatio : undefined,
}
return {
reply: 'Lanzo a la cola el vídeo ya preparado en Studio. 🪄',
diff --git a/ui/tests/agentActions.test.mjs b/ui/tests/agentActions.test.mjs
--- a/ui/tests/agentActions.test.mjs
+++ b/ui/tests/agentActions.test.mjs
@@ -1332,17 +1332,51 @@
test('genera el video reuses a prepared Studio prompt instead of asking for a topic', async () => {
const { useStore } = await import('../src/stores/useStore.ts')
const { reconcileAgentTurnWithRequest } = await import('../src/features/agent/agentActions.ts')
+ const original = {
+ generationMode: useStore.getState().generationMode,
+ params: useStore.getState().params,
+ durationSeconds: useStore.getState().durationSeconds,
+ savedPromptPerMode: useStore.getState().savedPromptPerMode,
+ }
useStore.setState({
+ generationMode: 'video',
params: { ...useStore.getState().params, prompt: 'Sheldon Cooper cuenta un chiste en su salón, bata verde, Bazinga.' },
durationSeconds: 5.2,
})
const resumed = await reconcileAgentTurnWithRequest('genera el video', { reply: '¿De qué?', actions: [] })
assert.deepEqual(resumed.actions.map(action => action.type), ['prepare_video', 'start_generation'])
assert.equal(resumed.actions[0].prompt.includes('Sheldon'), true)
+ assert.equal(resumed.actions[0].modelType, undefined)
assert.equal(resumed.actions[1].confirm, true)
- useStore.setState({ params: { ...useStore.getState().params, prompt: '' } })
+ const topical = await reconcileAgentTurnWithRequest('genera el video de un mapache', { reply: '¿De qué?', actions: [] })
+ assert.deepEqual(topical.actions.map(action => action.type), ['prepare_video', 'start_generation'])
+ assert.ok(topical.actions[0].prompt.includes('mapache'))
+ assert.equal(topical.actions[0].prompt.includes('Sheldon'), false)
+
+ const example = await reconcileAgentTurnWithRequest('genera el video de ejemplo', { reply: '¿De qué?', actions: [] })
+ assert.deepEqual(example.actions.map(action => action.type), ['prepare_video', 'start_generation'])
+ assert.ok(example.actions[0].prompt.length > 40)
+ assert.equal(example.actions[0].prompt.includes('genera el video'), false)
+ assert.equal(example.actions[0].prompt.includes('Sheldon'), false)
+
+ useStore.setState({
+ generationMode: 'image',
+ params: { ...useStore.getState().params, model_type: 'image-only-model', prompt: 'un retrato de estudio' },
+ savedPromptPerMode: { ...useStore.getState().savedPromptPerMode, video: 'Sheldon Cooper cuenta un chiste en su salón, bata verde, Bazinga.' },
+ })
+ const afterModeSwitch = await reconcileAgentTurnWithRequest('genera el video', { reply: '¿De qué?', actions: [] })
+ assert.deepEqual(afterModeSwitch.actions.map(action => action.type), ['prepare_video', 'start_generation'])
+ assert.equal(afterModeSwitch.actions[0].prompt.includes('Sheldon'), true)
+ assert.equal(afterModeSwitch.actions[0].modelType, undefined)
+
+ useStore.setState({
+ generationMode: 'video',
+ params: { ...useStore.getState().params, prompt: '' },
+ savedPromptPerMode: { ...useStore.getState().savedPromptPerMode, video: '' },
+ })
const asked = await reconcileAgentTurnWithRequest('genera el video', { reply: '¿De qué?', actions: [] })
assert.equal(asked.actions[0].type, 'open_tab')
assert.equal(asked.actions.some(action => action.type === 'start_generation'), false)
+ useStore.setState(original)
})You can send follow-ups to the cloud agent here.
|
cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 2a9c53f. Configure here.
Bare “hazme un video” still asks for a topic. “genera el video” reuses the current Studio prompt and emits prepare+start in the same turn so the runner will actually enqueue.
Bare "genera el video" still queues the prepared form, but leftover subject text now falls through to example/topical generation. Resume also omits the current model id and reads the saved Video prompt after a mode switch so I2V or Image leftovers do not fail prepareVideo.
Cherry-pick Cursor Autofix, then split the tests: resume the prepared form, use a new topic when one is named, and never copy an I2V/audio/3D model as T2V.
2a9c53f to
37bb43b
Compare
|
cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 37bb43b. Configure here.

Qué
Arregla el caso Sheldon: tras
prepare_video, «genera el video» preguntaba el tema y solo abría Studio.prepare_video+start_generationen el mismo turno (el runner no aceptastartsuelto).No toca slices. Independiente de #42–#45.
Verificación
agentActions.test.mjs52 pass, incluido el caso Sheldon.Dueño del merge: humano. No auto-merge.
Note
Low Risk
Localized agent intent reconciliation and tests only; no queue, auth, or persistence changes beyond reusing existing Studio store fields.
Overview
Fixes the post-
prepare_videoloop where «genera el video» made the agent ask for a topic instead of enqueueing the form already filled in Studio.Adds
isResumePreparedStudioVideoRequestto recognize narrow “launch/queue the video” phrasing (ES/EN) with no new subject after the noun.reconcileAgentTurnWithRequesthandles that before example/topical video repair: it pulls the prompt from an existingprepare_videoaction or from Studio state (params.prompt/savedPromptPerMode.video), returnsprepare_video+start_generation(confirm: true) when a prompt exists, oropen_tab→ studio when it does not.modelTypeis not copied from the current form so image/audio/3D/I2V selections are not forced onto T2V.Bare «hazme un video» and requests with extra topic (e.g. «genera el video de un mapache») are unchanged. Tests cover the Sheldon case, topical override, and incompatible models.
Reviewed by Cursor Bugbot for commit 37bb43b. Configure here.