unified asset library

This commit is contained in:
vegu-ai-tools
2025-11-16 13:38:55 +02:00
parent 6890f4f138
commit eaa9f76181
7 changed files with 610 additions and 245 deletions

View File

@@ -56,8 +56,9 @@ class AnalysisMixin:
if response.request and response.request.asset_id and response.analysis:
scene = active_scene.get()
if scene and response.request.asset_id in scene.assets.assets:
asset = scene.assets.assets[response.request.asset_id]
asset = scene.assets.get_asset(response.request.asset_id)
asset.meta.analysis = response.analysis
scene.assets.update_asset_meta(response.request.asset_id, asset.meta)
scene.saved = False
scene.emit_status()

View File

@@ -83,8 +83,8 @@
"title": "List Collector",
"id": "78179ea2-5b0e-4a3f-bd16-cf7a0335459b",
"properties": {},
"x": 1365,
"y": 1231,
"x": 1337,
"y": 1666,
"width": 140,
"height": 81,
"collapsed": false,
@@ -120,8 +120,8 @@
"response_length": 0,
"technical": false
},
"x": 1555,
"y": 971,
"x": 1527,
"y": 1406,
"width": 304,
"height": 542,
"collapsed": false,
@@ -157,21 +157,6 @@
"registry": "core/functions/DefineFunction",
"base_type": "core/Node"
},
"19e58d48-e6b2-4444-b561-d2d04f2caf3b": {
"title": "FN asset_info",
"id": "19e58d48-e6b2-4444-b561-d2d04f2caf3b",
"properties": {
"name": "asset_info"
},
"x": 485,
"y": 1301,
"width": 210,
"height": 78,
"collapsed": false,
"inherited": false,
"registry": "core/functions/GetFunction",
"base_type": "core/Node"
},
"047adc9f-f498-42e1-8f5a-2559aa4dcbaf": {
"title": "Get Asset",
"id": "047adc9f-f498-42e1-8f5a-2559aa4dcbaf",
@@ -194,7 +179,7 @@
"x": -941,
"y": 1011,
"width": 178,
"height": 286,
"height": 306,
"collapsed": false,
"inherited": false,
"registry": "assets/UnpackAssetMeta",
@@ -236,8 +221,8 @@
"header": "Assets",
"content": null
},
"x": 1095,
"y": 1271,
"x": 1067,
"y": 1706,
"width": 228,
"height": 102,
"collapsed": false,
@@ -245,22 +230,6 @@
"registry": "agents/DynamicInstruction",
"base_type": "core/Node"
},
"b922d276-1b60-4494-97ec-092916ac0883": {
"title": "Call For Each",
"id": "b922d276-1b60-4494-97ec-092916ac0883",
"properties": {
"copy_items": false,
"argument_name": "item"
},
"x": 805,
"y": 1351,
"width": 210,
"height": 122,
"collapsed": false,
"inherited": false,
"registry": "core/functions/CallForEach",
"base_type": "core/Node"
},
"a2b4a51d-c353-4961-9189-e53dd7b8a4cc": {
"title": "Return",
"id": "a2b4a51d-c353-4961-9189-e53dd7b8a4cc",
@@ -324,8 +293,8 @@
"properties": {
"template": "Scan the `image generation prompt` and determine up to {{ max_references }} to link to the image generation process.\n\nReferences should be used to provide visual guidance only.\n\nHighest priority are character likeness, followed by environment and style.\n\nFor each reference provide a short instruction on what the reference should inform.\n\nAnalyze both the prompt and the references before making your decision.\n\nYour preference should be to select one reference per subject, and your goal should not be to exhaust your reference alloweance at all costs.\n\n### Image generation prompt\n\n``` prompt\n{{ prompt }}\n```"
},
"x": 1105,
"y": 1021,
"x": 1077,
"y": 1456,
"width": 210,
"height": 153,
"collapsed": false,
@@ -343,19 +312,6 @@
],
"base_type": "core/DynamicSocketNodeBase"
},
"50a26db5-a93e-4690-ac5c-89fff54dd734": {
"title": "Backend Status",
"id": "50a26db5-a93e-4690-ac5c-89fff54dd734",
"properties": {},
"x": 875,
"y": 1591,
"width": 170,
"height": 66,
"collapsed": false,
"inherited": false,
"registry": "agents/visual/BackendStatus",
"base_type": "core/Node"
},
"9071af02-b26e-4507-8d15-cd4d4c977bd3": {
"title": "Dict Collector",
"id": "9071af02-b26e-4507-8d15-cd4d4c977bd3",
@@ -413,29 +369,14 @@
"registry": "focal/Argument",
"base_type": "core/Node"
},
"7051e54e-1189-4d2f-9818-aa55603aa358": {
"title": "Stage 0",
"id": "7051e54e-1189-4d2f-9818-aa55603aa358",
"properties": {
"stage": 0
},
"x": 2905,
"y": 1511,
"width": 210,
"height": 118,
"collapsed": false,
"inherited": false,
"registry": "core/Stage",
"base_type": "core/Node"
},
"e4c27566-d5d5-44d4-8dd5-3dbdb856bcb9": {
"title": "Collect AI Function Call Results",
"id": "e4c27566-d5d5-44d4-8dd5-3dbdb856bcb9",
"properties": {
"name": "select_reference"
},
"x": 2250,
"y": 1520,
"x": 2222,
"y": 1955,
"width": 269,
"height": 78,
"collapsed": false,
@@ -452,8 +393,8 @@
"retries": 0,
"response_length": 1024
},
"x": 1992,
"y": 1501,
"x": 1964,
"y": 1936,
"width": 210,
"height": 250,
"collapsed": false,
@@ -651,73 +592,6 @@
"registry": "core/Input",
"base_type": "core/Node"
},
"0f3a0ab4-45bd-4deb-b14e-ab6b673b4d0f": {
"title": "Search Assets",
"id": "0f3a0ab4-45bd-4deb-b14e-ab6b673b4d0f",
"properties": {
"vis_type": "",
"character_name": "",
"tags": [],
"reference_vis_types": [],
"tag_match_mode": "all",
"references_only": true
},
"x": 445,
"y": 1451,
"width": 329,
"height": 274,
"collapsed": false,
"inherited": false,
"registry": "assets/SearchAssets",
"base_type": "core/Node"
},
"2cdd9993-bb7b-4e2d-8242-58a96d659494": {
"title": "SET local.selected_references",
"id": "2cdd9993-bb7b-4e2d-8242-58a96d659494",
"properties": {
"name": "selected_references",
"scope": "local"
},
"x": 2580,
"y": 1500,
"width": 286,
"height": 122,
"collapsed": false,
"inherited": false,
"registry": "state/SetState",
"base_type": "core/Node"
},
"7abcc346-319c-47a7-93dc-f9392242d5e8": {
"title": "summarizer",
"id": "7abcc346-319c-47a7-93dc-f9392242d5e8",
"properties": {
"agent_name": "summarizer"
},
"x": 1340,
"y": 970,
"width": 210,
"height": 58,
"collapsed": true,
"inherited": false,
"registry": "agents/GetAgent",
"base_type": "core/Node"
},
"bf1b63b3-acb3-42a4-bcc7-0959597bc005": {
"title": "GET local.prompt",
"id": "bf1b63b3-acb3-42a4-bcc7-0959597bc005",
"properties": {
"name": "prompt",
"scope": "local"
},
"x": 790,
"y": 1030,
"width": 210,
"height": 122,
"collapsed": false,
"inherited": false,
"registry": "state/GetState",
"base_type": "core/Node"
},
"db5acc2a-4c09-4792-b503-f064f2ef4ea8": {
"title": "IN vis_type",
"id": "db5acc2a-4c09-4792-b503-f064f2ef4ea8",
@@ -817,41 +691,6 @@
"registry": "state/SetState",
"base_type": "core/Node"
},
"3429402d-fc1d-46cf-b159-3aa28673b054": {
"title": "GET local.vis_type",
"id": "3429402d-fc1d-46cf-b159-3aa28673b054",
"properties": {
"name": "vis_type",
"scope": "local"
},
"x": 40,
"y": 1510,
"width": 210,
"height": 122,
"collapsed": false,
"inherited": false,
"registry": "state/GetState",
"base_type": "core/Node"
},
"4c9d0de8-cb9c-4b87-9c06-d4031bb76317": {
"title": "List Collector",
"id": "4c9d0de8-cb9c-4b87-9c06-d4031bb76317",
"properties": {},
"x": 280,
"y": 1520,
"width": 140,
"height": 81,
"collapsed": false,
"inherited": false,
"registry": "data/ListCollector",
"dynamic_inputs": [
{
"name": "item0",
"type": "*"
}
],
"base_type": "core/DynamicSocketNodeBase"
},
"4e079b60-a8fc-408e-8dcb-b8f7a08017a0": {
"title": "AI Function Callback Metadata",
"id": "4e079b60-a8fc-408e-8dcb-b8f7a08017a0",
@@ -873,6 +712,153 @@
"registry": "focal/Metadata",
"base_type": "core/Node"
},
"19e58d48-e6b2-4444-b561-d2d04f2caf3b": {
"title": "FN asset_info",
"id": "19e58d48-e6b2-4444-b561-d2d04f2caf3b",
"properties": {
"name": "asset_info"
},
"x": 782,
"y": 1755,
"width": 210,
"height": 78,
"collapsed": true,
"inherited": false,
"registry": "core/functions/GetFunction",
"base_type": "core/Node"
},
"b922d276-1b60-4494-97ec-092916ac0883": {
"title": "Call For Each",
"id": "b922d276-1b60-4494-97ec-092916ac0883",
"properties": {
"copy_items": false,
"argument_name": "item"
},
"x": 777,
"y": 1786,
"width": 210,
"height": 122,
"collapsed": false,
"inherited": false,
"registry": "core/functions/CallForEach",
"base_type": "core/Node"
},
"1bd33d57-b320-4e0a-a3e6-b88d999e1af6": {
"title": "GET local.vis_type",
"id": "1bd33d57-b320-4e0a-a3e6-b88d999e1af6",
"properties": {
"name": "vis_type",
"scope": "local"
},
"x": 48,
"y": 1031,
"width": 210,
"height": 122,
"collapsed": false,
"inherited": false,
"registry": "state/GetState",
"base_type": "core/Node"
},
"3d9ef292-f08a-45ee-bb0f-265056c29980": {
"title": "List Collector",
"id": "3d9ef292-f08a-45ee-bb0f-265056c29980",
"properties": {},
"x": 288,
"y": 1041,
"width": 140,
"height": 81,
"collapsed": false,
"inherited": false,
"registry": "data/ListCollector",
"dynamic_inputs": [
{
"name": "item0",
"type": "*"
}
],
"base_type": "core/DynamicSocketNodeBase"
},
"58d88870-548a-45da-a3b5-f7f254168f9f": {
"title": "SET local.has_references",
"id": "58d88870-548a-45da-a3b5-f7f254168f9f",
"properties": {
"name": "has_references",
"scope": "local"
},
"x": 1100,
"y": 1107,
"width": 210,
"height": 122,
"collapsed": false,
"inherited": false,
"registry": "state/SetState",
"base_type": "core/Node"
},
"3085f6fc-13be-4788-a318-2b741c7fd82c": {
"title": "SET local.references",
"id": "3085f6fc-13be-4788-a318-2b741c7fd82c",
"properties": {
"name": "references",
"scope": "local"
},
"x": 1090,
"y": 887,
"width": 210,
"height": 122,
"collapsed": false,
"inherited": false,
"registry": "state/SetState",
"base_type": "core/Node"
},
"c327d4f3-8334-4cda-afcb-abce468a3f63": {
"title": "Stage 0",
"id": "c327d4f3-8334-4cda-afcb-abce468a3f63",
"properties": {
"stage": 0
},
"x": 1410,
"y": 1007,
"width": 210,
"height": 118,
"collapsed": false,
"inherited": false,
"registry": "core/Stage",
"base_type": "core/Node"
},
"0fd69b8a-4c7b-4ddb-8a91-0826e811c115": {
"title": "Switch",
"id": "0fd69b8a-4c7b-4ddb-8a91-0826e811c115",
"properties": {
"pass_through": false
},
"x": 342,
"y": 1400,
"width": 210,
"height": 78,
"collapsed": false,
"inherited": false,
"registry": "core/Switch",
"base_type": "core/Node"
},
"98c75f89-48cb-4645-84cf-e7f6512af35a": {
"title": "List Collector",
"id": "98c75f89-48cb-4645-84cf-e7f6512af35a",
"properties": {},
"x": 1605,
"y": 2230,
"width": 140,
"height": 81,
"collapsed": false,
"inherited": false,
"registry": "data/ListCollector",
"dynamic_inputs": [
{
"name": "item0",
"type": "*"
}
],
"base_type": "core/DynamicSocketNodeBase"
},
"14ebd09d-3fc2-4149-b8df-87f4081be551": {
"title": "AI Function Callback",
"id": "14ebd09d-3fc2-4149-b8df-87f4081be551",
@@ -880,8 +866,8 @@
"name": "my_function",
"allow_multiple_calls": true
},
"x": 1315,
"y": 1981,
"x": 1345,
"y": 2220,
"width": 210,
"height": 102,
"collapsed": false,
@@ -895,8 +881,8 @@
"properties": {
"name": "select_reference"
},
"x": 1030,
"y": 1980,
"x": 1075,
"y": 2220,
"width": 210,
"height": 78,
"collapsed": false,
@@ -904,24 +890,150 @@
"registry": "core/functions/GetFunction",
"base_type": "core/Node"
},
"98c75f89-48cb-4645-84cf-e7f6512af35a": {
"title": "List Collector",
"id": "98c75f89-48cb-4645-84cf-e7f6512af35a",
"50a26db5-a93e-4690-ac5c-89fff54dd734": {
"title": "Backend Status",
"id": "50a26db5-a93e-4690-ac5c-89fff54dd734",
"properties": {},
"x": 1620,
"y": 1960,
"width": 140,
"height": 81,
"x": 795,
"y": 2020,
"width": 170,
"height": 66,
"collapsed": false,
"inherited": false,
"registry": "data/ListCollector",
"dynamic_inputs": [
{
"name": "item0",
"type": "*"
}
],
"base_type": "core/DynamicSocketNodeBase"
"registry": "agents/visual/BackendStatus",
"base_type": "core/Node"
},
"50a7431a-2410-4b99-b7eb-13dbf06d2775": {
"title": "GET local.references",
"id": "50a7431a-2410-4b99-b7eb-13dbf06d2775",
"properties": {
"name": "references",
"scope": "local"
},
"x": 568,
"y": 1850,
"width": 210,
"height": 122,
"collapsed": true,
"inherited": false,
"registry": "state/GetState",
"base_type": "core/Node"
},
"bf1b63b3-acb3-42a4-bcc7-0959597bc005": {
"title": "GET local.prompt",
"id": "bf1b63b3-acb3-42a4-bcc7-0959597bc005",
"properties": {
"name": "prompt",
"scope": "local"
},
"x": 878,
"y": 1530,
"width": 210,
"height": 122,
"collapsed": true,
"inherited": false,
"registry": "state/GetState",
"base_type": "core/Node"
},
"43dcf513-8229-493d-9503-c2fe79a68d18": {
"title": "GET local.has_references",
"id": "43dcf513-8229-493d-9503-c2fe79a68d18",
"properties": {
"name": "has_references",
"scope": "local"
},
"x": 108,
"y": 1430,
"width": 271,
"height": 124,
"collapsed": true,
"inherited": false,
"registry": "state/GetState",
"base_type": "core/Node"
},
"7abcc346-319c-47a7-93dc-f9392242d5e8": {
"title": "summarizer",
"id": "7abcc346-319c-47a7-93dc-f9392242d5e8",
"properties": {
"agent_name": "summarizer"
},
"x": 1380,
"y": 1460,
"width": 210,
"height": 58,
"collapsed": true,
"inherited": false,
"registry": "agents/GetAgent",
"base_type": "core/Node"
},
"bf3b8b75-ac0f-4e36-a33e-5a7596dfb398": {
"title": "Search Assets",
"id": "bf3b8b75-ac0f-4e36-a33e-5a7596dfb398",
"properties": {
"vis_type": "",
"character_name": "",
"tags": [],
"reference_vis_types": [],
"tag_match_mode": "all",
"references_only": true
},
"x": 453,
"y": 972,
"width": 329,
"height": 274,
"collapsed": false,
"inherited": false,
"registry": "assets/SearchAssets",
"base_type": "core/Node"
},
"83ce46ec-45ca-49d0-9bc7-003a855d881c": {
"title": "Compare",
"id": "83ce46ec-45ca-49d0-9bc7-003a855d881c",
"properties": {
"operation": "greater_than",
"tolerance": 0.0001,
"a": 0,
"b": 0
},
"x": 840,
"y": 1107,
"width": 210,
"height": 150,
"collapsed": false,
"inherited": false,
"registry": "data/number/Compare",
"base_type": "core/Node"
},
"2cdd9993-bb7b-4e2d-8242-58a96d659494": {
"title": "SET local.selected_references",
"id": "2cdd9993-bb7b-4e2d-8242-58a96d659494",
"properties": {
"name": "selected_references",
"scope": "local"
},
"x": 2540,
"y": 1940,
"width": 286,
"height": 122,
"collapsed": false,
"inherited": false,
"registry": "state/SetState",
"base_type": "core/Node"
},
"7051e54e-1189-4d2f-9818-aa55603aa358": {
"title": "Stage 1",
"id": "7051e54e-1189-4d2f-9818-aa55603aa358",
"properties": {
"stage": 1
},
"x": 2870,
"y": 1940,
"width": 210,
"height": 118,
"collapsed": false,
"inherited": false,
"registry": "core/Stage",
"base_type": "core/Node"
}
},
"edges": {
@@ -946,9 +1058,6 @@
"9e43c1f7-dbec-4a10-87d6-bb88f68d02b4.value": [
"598f5544-5ce3-4a3e-a096-84870fafbb7d.nodes"
],
"19e58d48-e6b2-4444-b561-d2d04f2caf3b.fn": [
"b922d276-1b60-4494-97ec-092916ac0883.fn"
],
"047adc9f-f498-42e1-8f5a-2559aa4dcbaf.asset_id": [
"ac5dc0bc-dfb8-4f5f-aa68-74b2a90a953e.item0"
],
@@ -967,9 +1076,6 @@
"48a66502-eed2-4995-9dce-7b68e28a911f.dynamic_instruction": [
"78179ea2-5b0e-4a3f-bd16-cf7a0335459b.item0"
],
"b922d276-1b60-4494-97ec-092916ac0883.results": [
"48a66502-eed2-4995-9dce-7b68e28a911f.content"
],
"a2b4a51d-c353-4961-9189-e53dd7b8a4cc.value": [
"4e079b60-a8fc-408e-8dcb-b8f7a08017a0.state"
],
@@ -982,13 +1088,6 @@
"8b5a72e0-cca6-49ff-bbe2-14f0e78da8d5.result": [
"92d1273b-79d8-4898-b00a-be56ea676246.instructions"
],
"50a26db5-a93e-4690-ac5c-89fff54dd734.can_edit_images": [
"92d1273b-79d8-4898-b00a-be56ea676246.state"
],
"50a26db5-a93e-4690-ac5c-89fff54dd734.max_references": [
"8b5a72e0-cca6-49ff-bbe2-14f0e78da8d5.item1",
"08cc0883-df2e-47c8-84f1-21ebcf4a1d99.max_calls"
],
"9071af02-b26e-4507-8d15-cd4d4c977bd3.dict": [
"a2b4a51d-c353-4961-9189-e53dd7b8a4cc.value"
],
@@ -1030,19 +1129,6 @@
"760b9b69-263b-4263-a14f-fc9dee596247.value": [
"a027e49c-9f36-4746-b797-98655b862c65.value"
],
"0f3a0ab4-45bd-4deb-b14e-ab6b673b4d0f.asset_ids": [
"b922d276-1b60-4494-97ec-092916ac0883.state",
"b922d276-1b60-4494-97ec-092916ac0883.items"
],
"2cdd9993-bb7b-4e2d-8242-58a96d659494.scope": [
"7051e54e-1189-4d2f-9818-aa55603aa358.state"
],
"7abcc346-319c-47a7-93dc-f9392242d5e8.agent": [
"92d1273b-79d8-4898-b00a-be56ea676246.agent"
],
"bf1b63b3-acb3-42a4-bcc7-0959597bc005.value": [
"8b5a72e0-cca6-49ff-bbe2-14f0e78da8d5.item0"
],
"db5acc2a-4c09-4792-b503-f064f2ef4ea8.value": [
"0c306509-fdee-4d2a-ae35-ad0500ed0456.value"
],
@@ -1061,15 +1147,34 @@
"0c306509-fdee-4d2a-ae35-ad0500ed0456.value": [
"b23b9f1c-823a-4e3d-a592-45621cbb5f05.state_c"
],
"3429402d-fc1d-46cf-b159-3aa28673b054.value": [
"4c9d0de8-cb9c-4b87-9c06-d4031bb76317.item0"
],
"4c9d0de8-cb9c-4b87-9c06-d4031bb76317.list": [
"0f3a0ab4-45bd-4deb-b14e-ab6b673b4d0f.reference_vis_types"
],
"4e079b60-a8fc-408e-8dcb-b8f7a08017a0.state": [
"5fe01e1b-2eff-4e36-b141-073e4c75297c.nodes"
],
"19e58d48-e6b2-4444-b561-d2d04f2caf3b.fn": [
"b922d276-1b60-4494-97ec-092916ac0883.fn"
],
"b922d276-1b60-4494-97ec-092916ac0883.results": [
"48a66502-eed2-4995-9dce-7b68e28a911f.content"
],
"1bd33d57-b320-4e0a-a3e6-b88d999e1af6.value": [
"3d9ef292-f08a-45ee-bb0f-265056c29980.item0"
],
"3d9ef292-f08a-45ee-bb0f-265056c29980.list": [
"bf3b8b75-ac0f-4e36-a33e-5a7596dfb398.reference_vis_types"
],
"58d88870-548a-45da-a3b5-f7f254168f9f.value": [
"c327d4f3-8334-4cda-afcb-abce468a3f63.state_b"
],
"3085f6fc-13be-4788-a318-2b741c7fd82c.value": [
"c327d4f3-8334-4cda-afcb-abce468a3f63.state"
],
"0fd69b8a-4c7b-4ddb-8a91-0826e811c115.yes": [
"b922d276-1b60-4494-97ec-092916ac0883.state",
"92d1273b-79d8-4898-b00a-be56ea676246.state"
],
"98c75f89-48cb-4645-84cf-e7f6512af35a.list": [
"08cc0883-df2e-47c8-84f1-21ebcf4a1d99.callbacks"
],
"14ebd09d-3fc2-4149-b8df-87f4081be551.callback": [
"98c75f89-48cb-4645-84cf-e7f6512af35a.item0"
],
@@ -1079,8 +1184,33 @@
"0d7accb5-ecc5-4282-8a4c-4a9ef2660b6e.name": [
"14ebd09d-3fc2-4149-b8df-87f4081be551.name"
],
"98c75f89-48cb-4645-84cf-e7f6512af35a.list": [
"08cc0883-df2e-47c8-84f1-21ebcf4a1d99.callbacks"
"50a26db5-a93e-4690-ac5c-89fff54dd734.max_references": [
"8b5a72e0-cca6-49ff-bbe2-14f0e78da8d5.item1",
"08cc0883-df2e-47c8-84f1-21ebcf4a1d99.max_calls"
],
"50a7431a-2410-4b99-b7eb-13dbf06d2775.value": [
"b922d276-1b60-4494-97ec-092916ac0883.items"
],
"bf1b63b3-acb3-42a4-bcc7-0959597bc005.value": [
"8b5a72e0-cca6-49ff-bbe2-14f0e78da8d5.item0"
],
"43dcf513-8229-493d-9503-c2fe79a68d18.value": [
"0fd69b8a-4c7b-4ddb-8a91-0826e811c115.value"
],
"7abcc346-319c-47a7-93dc-f9392242d5e8.agent": [
"92d1273b-79d8-4898-b00a-be56ea676246.agent"
],
"bf3b8b75-ac0f-4e36-a33e-5a7596dfb398.asset_ids": [
"3085f6fc-13be-4788-a318-2b741c7fd82c.value"
],
"bf3b8b75-ac0f-4e36-a33e-5a7596dfb398.asset_count": [
"83ce46ec-45ca-49d0-9bc7-003a855d881c.a"
],
"83ce46ec-45ca-49d0-9bc7-003a855d881c.result": [
"58d88870-548a-45da-a3b5-f7f254168f9f.value"
],
"2cdd9993-bb7b-4e2d-8242-58a96d659494.scope": [
"7051e54e-1189-4d2f-9818-aa55603aa358.state"
]
},
"groups": [
@@ -1096,10 +1226,10 @@
},
{
"title": "Process",
"x": 13,
"y": 815,
"width": 3294,
"height": 1385,
"x": 18,
"y": 1290,
"width": 3094,
"height": 1057,
"color": "#3f789e",
"font_size": 24,
"inherited": false
@@ -1143,9 +1273,27 @@
"color": "#b06634",
"font_size": 24,
"inherited": false
},
{
"title": "Prepare",
"x": 23,
"y": 812,
"width": 1622,
"height": 470,
"color": "#8AA",
"font_size": 24,
"inherited": false
}
],
"comments": [
{
"text": "Determine if there are any reference candidates for this request.",
"x": 60,
"y": 870,
"width": 200,
"inherited": false
}
],
"comments": [],
"extends": null,
"base_type": "core/Graph",
"inputs": [],

View File

@@ -98,7 +98,8 @@ class VisualWebsocketHandler(Plugin):
sampler_settings=payload.generation_request.sampler_settings,
reference_assets=payload.generation_request.reference_assets,
)
scene.assets.assets[asset.id].meta = meta
# Update asset meta and save to library.json
scene.assets.update_asset_meta(asset.id, meta)
# Notify frontend and update scene status
scene.emit_status()

View File

@@ -5,6 +5,8 @@ import hashlib
import os
import enum
import io
import json
from pathlib import Path
from typing import TYPE_CHECKING
import pydantic
@@ -24,6 +26,7 @@ from talemate.agents.visual.schema import (
SamplerSettings,
Resolution,
)
from talemate.path import SCENES_DIR
__all__ = [
"Asset",
@@ -39,6 +42,7 @@ __all__ = [
"set_character_cover_image_from_image_data",
"set_character_cover_image_from_file_path",
"set_character_cover_image",
"migrate_scene_assets_to_library",
]
log = structlog.get_logger("talemate.scene_assets")
@@ -131,7 +135,7 @@ class Asset(pydantic.BaseModel):
class SceneAssets:
def __init__(self, scene: Scene):
self.scene = scene
self.assets = {}
self._assets_cache = None
self.cover_image = None
@property
@@ -154,6 +158,76 @@ class SceneAssets:
return asset_path
@property
def _library_path(self) -> str:
"""
Returns the path to the unified library.json file.
"""
return os.path.join(self.asset_directory, "library.json")
def _load_library(self) -> dict:
"""
Loads the asset library from library.json.
Returns an empty dict if the file doesn't exist.
"""
library_path = self._library_path
if not os.path.exists(library_path):
return {}
try:
with open(library_path, "r", encoding="utf-8") as f:
data = json.load(f)
return data.get("assets", {})
except (json.JSONDecodeError, IOError) as e:
log.warning("Failed to load asset library", error=str(e), path=library_path)
return {}
def _save_library(self, assets_dict: dict):
"""
Saves the asset library to library.json.
"""
library_path = self._library_path
# Ensure directory exists
os.makedirs(os.path.dirname(library_path), exist_ok=True)
try:
with open(library_path, "w", encoding="utf-8") as f:
json.dump({"assets": assets_dict}, f, indent=2, default=str)
except IOError as e:
log.error("Failed to save asset library", error=str(e), path=library_path)
raise
@property
def assets(self) -> dict:
"""
Returns the assets dictionary, loading from library.json if needed.
"""
if self._assets_cache is None:
assets_dict = self._load_library()
self._assets_cache = {
asset_id: Asset(**asset_dict)
for asset_id, asset_dict in assets_dict.items()
}
return self._assets_cache
@assets.setter
def assets(self, value: dict):
"""
Sets the assets dictionary and saves to library.json.
"""
self._assets_cache = value
assets_dict = {
asset_id: asset.model_dump()
for asset_id, asset in value.items()
}
self._save_library(assets_dict)
def _invalidate_cache(self):
"""
Invalidates the assets cache, forcing a reload from library.json.
"""
self._assets_cache = None
def validate_asset_id(self, asset_id: str) -> bool:
"""
Validates that the asset id is a valid asset id.
@@ -180,11 +254,14 @@ class SceneAssets:
def load_assets(self, assets_dict: dict):
"""
Loads assets from a dictionary.
Legacy method kept for API compatibility.
Assets are now loaded from library.json automatically via the assets property.
Migration handles moving assets from scene files to library.json.
This method is a no-op since migration runs on server startup.
"""
for asset_id, asset_dict in assets_dict.items():
self.assets[asset_id] = Asset(**asset_dict)
# No-op: assets are loaded from library.json via the assets property
# Migration handles moving assets from scene files to library.json
pass
def transfer_asset(self, source: "SceneAssets", asset_id: str):
"""
@@ -228,7 +305,10 @@ class SceneAssets:
# create the asset object
asset = Asset(id=asset_id, file_type=file_extension, media_type=media_type)
self.assets[asset_id] = asset
# Add to assets (this will save to library.json)
current_assets = self.assets
current_assets[asset_id] = asset
self.assets = current_assets
return asset
@@ -301,6 +381,20 @@ class SceneAssets:
return self.assets[asset_id]
def update_asset_meta(self, asset_id: str, meta: AssetMeta):
"""
Updates the metadata for an asset and saves to library.json.
Args:
asset_id: The ID of the asset to update
meta: The new metadata to set
"""
current_assets = self.assets
if asset_id not in current_assets:
raise KeyError(f"Asset {asset_id} not found")
current_assets[asset_id].meta = meta
self.assets = current_assets # Save to library.json
def get_asset_bytes(self, asset_id: str) -> bytes | None:
"""
Returns the bytes of the asset with the given id.
@@ -385,7 +479,11 @@ class SceneAssets:
Removes the asset with the given id.
"""
asset = self.assets.pop(asset_id)
current_assets = self.assets
asset = current_assets.pop(asset_id)
# Save updated library
self.assets = current_assets
asset_path = self.asset_directory
@@ -563,6 +661,111 @@ async def set_scene_cover_image(
return asset_id
def migrate_scene_assets_to_library(root: Path | str | None = None) -> None:
"""
Migrates scene assets from individual scene files to a unified library.json file.
This function scans all scene JSON files in each project directory and collects
all assets into a single library.json file located at assets/library.json within
each project directory. This migration does not modify the scene files themselves.
Args:
root: Optional path to the root scenes directory. If None, uses SCENES_DIR.
"""
scenes_root = Path(root) if root else SCENES_DIR
if not scenes_root.is_dir():
log.warning("scenes_root_not_found", root=str(scenes_root))
return
processed_projects = 0
processed_scenes = 0
total_assets = 0
try:
# Iterate through all project directories
for project_path in sorted(
(p for p in scenes_root.iterdir() if p.is_dir()),
key=lambda p: p.name
):
# Find all scene JSON files in this project
scene_files = [
p for p in project_path.iterdir()
if p.is_file() and p.suffix == ".json"
]
if not scene_files:
continue
# Collect all assets from all scene files
all_assets = {}
for scene_file in scene_files:
try:
with open(scene_file, "r", encoding="utf-8") as f:
scene_data = json.load(f)
# Extract assets from scene data
assets_data = scene_data.get("assets", {}).get("assets", {})
if assets_data:
# Merge assets into the unified collection
# Later scenes will override earlier ones if same asset_id exists
all_assets.update(assets_data)
processed_scenes += 1
except (json.JSONDecodeError, IOError) as e:
log.warning(
"migrate_scene_assets_failed_to_read",
scene_file=str(scene_file),
error=str(e)
)
continue
# If we found any assets, create the library.json file
if all_assets:
assets_dir = project_path / "assets"
assets_dir.mkdir(exist_ok=True)
library_path = assets_dir / "library.json"
# Only create if it doesn't exist (idempotent migration)
if not library_path.exists():
try:
with open(library_path, "w", encoding="utf-8") as f:
json.dump({"assets": all_assets}, f, indent=2, default=str)
processed_projects += 1
total_assets += len(all_assets)
log.debug(
"migrated_scene_assets_to_library",
project=str(project_path.name),
assets_count=len(all_assets),
library_path=str(library_path)
)
except IOError as e:
log.error(
"migrate_scene_assets_failed_to_write",
library_path=str(library_path),
error=str(e)
)
else:
log.debug(
"library_already_exists",
project=str(project_path.name),
library_path=str(library_path)
)
except Exception as e:
log.error("migrate_scene_assets_to_library_failed", error=str(e))
if processed_projects > 0:
log.info(
"migration_complete",
projects_processed=processed_projects,
scenes_processed=processed_scenes,
total_assets=total_assets
)
async def set_character_cover_image_from_bytes(
scene: "Scene", character: "Character", bytes: bytes, override: bool = False
) -> str:

View File

@@ -131,6 +131,7 @@ def run_server(args):
from talemate.emit.base import emit
import talemate.agents.tts.voice_library as voice_library
from talemate.changelog import ensure_changelogs_for_all_scenes
from talemate.scene_assets import migrate_scene_assets_to_library
# import node libraries
import talemate.game.engine.nodes.load_definitions
@@ -187,6 +188,9 @@ def run_server(args):
# create task to ensure changelogs for all scenes exists
loop.create_task(ensure_changelogs_for_all_scenes())
# migrate scene assets to unified library.json files
migrate_scene_assets_to_library()
# start task to unstall punkt
loop.create_task(install_punkt())

View File

@@ -97,7 +97,7 @@ class SceneAssetsPlugin(Plugin):
)
# Assign meta and emit
self.scene.assets.assets[asset.id].meta = meta
self.scene.assets.update_asset_meta(asset.id, meta)
# notify frontend
await self.scene.attempt_auto_save()
@@ -161,7 +161,7 @@ class SceneAssetsPlugin(Plugin):
log.error("reevaluate_format_failed", error=e)
# Assign back
self.scene.assets.assets[asset_id].meta = meta
self.scene.assets.update_asset_meta(asset_id, meta)
# Notify
await self.scene.attempt_auto_save()

View File

@@ -141,9 +141,17 @@ def mock_scene_with_assets():
scene.scenes_dir = lambda: test_scenes_dir
scene.project_name = "talemate-laboratory"
# Load assets into the scene
# Create library.json file with assets from the scene file
if "assets" in test_scene_data and "assets" in test_scene_data["assets"]:
scene.assets.load_assets(test_scene_data["assets"]["assets"])
assets_dict = test_scene_data["assets"]["assets"]
# Ensure assets directory exists
assets_dir = os.path.join(test_scenes_dir, "talemate-laboratory", "assets")
os.makedirs(assets_dir, exist_ok=True)
# Create library.json file
library_path = os.path.join(assets_dir, "library.json")
with open(library_path, "w") as f:
json.dump({"assets": assets_dict}, f, indent=2)
return scene