fix(nodes): Save pair also gives a new prompt its category and tags (#546)
The node's Save pair button posts to /prompt_manager/pairs, which created the prompt row empty; under the fill-on-create rule a later run could never fill it.
This commit is contained in:
+3
-3
@@ -2890,11 +2890,11 @@ change: ComfyUI needs a restart.
|
||||
|
||||
### Pair node gives a new prompt its category and tags (#546)
|
||||
|
||||
A Pair save writes the category and tags to the positive prompt only when that save creates the prompt row (maintainer
|
||||
ruling). An existing prompt never changes on a run: empty, cleared or set by a person, including one first seen as
|
||||
A Pair save, by a run or the node's Save pair button, writes the category and tags to the positive prompt only when that
|
||||
save creates the prompt row (maintainer ruling). An existing prompt never changes on a run: empty, cleared or set by a person, including one first seen as
|
||||
another pair's negative. The negative stays empty, the saved pair keeps its own metadata, and `save_prompt_pair` writes
|
||||
no prompt metadata unless called with `fill_new_positive`. No backfill; optional repair is #550.
|
||||
Tests: `tests/test_prompt_pair_node.py`. A Python change: ComfyUI needs a restart.
|
||||
Tests: `tests/test_prompt_pair_node.py`, `tests/test_api_pairs.py`, `tests/comfyui/pair-node.spec.mjs`. A Python change: ComfyUI needs a restart.
|
||||
|
||||
### Delete… on the archived shelf (#492)
|
||||
|
||||
|
||||
+6
-1
@@ -102,7 +102,12 @@ class PairRoutesMixin:
|
||||
validate_prompt_text(negative)
|
||||
fields = metadata_fields(data)
|
||||
pair_id = await self._run_in_executor(
|
||||
self.db.save_prompt_pair, positive, negative, **fields
|
||||
self.db.save_prompt_pair,
|
||||
positive,
|
||||
negative,
|
||||
# The node's Save pair button: as a run, a new prompt gets the metadata.
|
||||
fill_new_positive=True,
|
||||
**fields,
|
||||
)
|
||||
pair = await self._run_in_executor(self.db.get_prompt_pair, pair_id)
|
||||
return web.json_response({"success": True, "pair": pair})
|
||||
|
||||
@@ -60,6 +60,15 @@ test('paired explicit Save persists an empty negative through actual API and pre
|
||||
await expect(root.getByRole('status').filter({hasText:'Pair saved.'})).toBeVisible();const response=await request.get(`/prompt_manager/pairs?text=${encodeURIComponent(text)}`);expect(response.ok()).toBe(true);expect((await response.json()).pairs).toEqual(expect.arrayContaining([expect.objectContaining({positive_text:text,negative_text:''})]));expect(await values(page,id)).toMatchObject({text,negative_text:''});
|
||||
});
|
||||
|
||||
test('Save pair gives a new prompt the category and tags shown on the node',async({page,request},testInfo)=>{
|
||||
await addPair(page);const root=page.locator('.pm-pair');const text=`Saved pair metadata ${testInfo.project.name}`;await root.getByRole('textbox',{name:'Positive prompt',exact:true}).fill(text);
|
||||
await root.getByText('Metadata · category / tags',{exact:true}).click();await root.getByRole('combobox',{name:'Category',exact:true}).fill('Saved category');await root.getByRole('combobox',{name:'Tags',exact:true}).fill('saved-tag');
|
||||
await root.getByRole('button',{name:'Save pair',exact:true}).click();await expect(root.getByRole('status').filter({hasText:'Pair saved.'})).toBeVisible();
|
||||
const [pair]=(await (await request.get(`/prompt_manager/pairs?text=${encodeURIComponent(text)}`)).json()).pairs;
|
||||
const library=await (await request.get(`/prompt_manager/library?text=${encodeURIComponent(text)}&limit=10`)).json();const prompt=library.items.find(item=>item.id===pair.positive_prompt_id);
|
||||
expect([prompt.category,prompt.tags]).toEqual(['Saved category',['saved-tag']]);
|
||||
});
|
||||
|
||||
test('paired library filters search both texts, persist on reload and keep editor metadata separate',async({page,request},testInfo)=>{
|
||||
const token=`paired-filter-${testInfo.project.name}`;await seed(request,`Matching ${token}`,`Negative ${token}`);const other=await request.post('/prompt_manager/pairs',{data:{positive_text:`Other ${token}`,negative_text:'different',category:'Other paired category',tags:['other-pair-tag']}});expect(other.ok()).toBe(true);
|
||||
const id=await addPair(page);const root=page.locator('.pm-pair');await root.getByRole('textbox',{name:'Positive prompt',exact:true}).fill('Draft positive');await root.getByRole('textbox',{name:'Negative prompt',exact:true}).fill('Draft negative');
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Pair routes preserve shared text and expose portable combinations."""
|
||||
|
||||
from tests.test_api_prompts import PromptAPITestCase
|
||||
from utils.hashing import generate_prompt_hash
|
||||
|
||||
|
||||
class TestPairAPI(PromptAPITestCase):
|
||||
@@ -9,6 +10,31 @@ class TestPairAPI(PromptAPITestCase):
|
||||
payload.update(overrides)
|
||||
return await self.client.post("/prompt_manager/pairs", json=payload)
|
||||
|
||||
async def _prompt_saved_by_route(self, text, **fields):
|
||||
response = await self._create(positive_text=text, **fields)
|
||||
self.assertEqual(response.status, 200)
|
||||
return self.api.db.get_prompt_by_hash(generate_prompt_hash(text))
|
||||
|
||||
async def test_a_new_prompt_gets_the_saved_category_and_tags(self):
|
||||
prompt = await self._prompt_saved_by_route(
|
||||
"route cat", category="Animals", tags=["studio"]
|
||||
)
|
||||
self.assertEqual(prompt["category"], "Animals")
|
||||
self.assertEqual(prompt["tags"], ["studio"])
|
||||
|
||||
async def test_saving_never_changes_an_existing_prompt(self):
|
||||
self.api.db.save_prompt(
|
||||
"route dog",
|
||||
category="Mine",
|
||||
tags=["kept"],
|
||||
prompt_hash=generate_prompt_hash("route dog"),
|
||||
)
|
||||
prompt = await self._prompt_saved_by_route(
|
||||
"route dog", category="Animals", tags=["studio"]
|
||||
)
|
||||
self.assertEqual(prompt["category"], "Mine")
|
||||
self.assertEqual(prompt["tags"], ["kept"])
|
||||
|
||||
async def test_create_reuse_read_and_search(self):
|
||||
response = await self._create(tags=["studio"], title="Portrait")
|
||||
self.assertEqual(response.status, 200)
|
||||
|
||||
Reference in New Issue
Block a user