mirror of
https://github.com/vegu-ai/talemate.git
synced 2026-08-29 10:08:58 +02:00
0.34.0 (#239)
Visual Agent Refactor + Visual Library Character Card Import Refactor Bug fixes and other improvements
This commit is contained in:
121
tests/data/graphs/results/test-harness-assets.json
Normal file
121
tests/data/graphs/results/test-harness-assets.json
Normal file
@@ -0,0 +1,121 @@
|
||||
{
|
||||
"TEST_001_001": "image/png",
|
||||
"TEST_001_002": {
|
||||
"name": "asset1",
|
||||
"vis_type": "SCENE_BACKGROUND",
|
||||
"gen_type": "TEXT_TO_IMAGE",
|
||||
"character_name": null,
|
||||
"prompt": "A futuristic laboratory for AI testing, in the style of digital concept art",
|
||||
"negative_prompt": "",
|
||||
"format": "SQUARE",
|
||||
"resolution": {
|
||||
"width": 1024,
|
||||
"height": 1024
|
||||
},
|
||||
"sampler_settings": {
|
||||
"steps": 40
|
||||
},
|
||||
"reference_assets": [],
|
||||
"tags": [],
|
||||
"reference": [
|
||||
"SCENE_BACKGROUND"
|
||||
],
|
||||
"analysis": null
|
||||
},
|
||||
"TEST_001_003": "SCENE_BACKGROUND",
|
||||
"TEST_001_004": {
|
||||
"width": 1024,
|
||||
"height": 1024
|
||||
},
|
||||
"TEST_002_001": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFUlEQVR42mP8z8BQz0AEYBxVSF+FABJADveWkH6oAAAAAElFTkSuQmCC",
|
||||
"TEST_002_002": {
|
||||
"name": "test_asset",
|
||||
"vis_type": "CHARACTER_CARD",
|
||||
"gen_type": "TEXT_TO_IMAGE",
|
||||
"character_name": null,
|
||||
"prompt": null,
|
||||
"negative_prompt": null,
|
||||
"format": "SQUARE",
|
||||
"resolution": {
|
||||
"width": 10,
|
||||
"height": 10
|
||||
},
|
||||
"sampler_settings": null,
|
||||
"reference_assets": [],
|
||||
"tags": [],
|
||||
"reference": [],
|
||||
"analysis": ""
|
||||
},
|
||||
"TEST_002_003": {
|
||||
"format": "SQUARE",
|
||||
"width": 10,
|
||||
"height": 10
|
||||
},
|
||||
"TEST_003_001": "png",
|
||||
"TEST_003_004": "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFUlEQVR42mP8z8BQz0AEYBxVSF+FABJADveWkH6oAAAAAElFTkSuQmCC",
|
||||
"TEST_003_002": "a9d80a755368f677990ac389c315c0628d50e951188ea15a72ab21272a1f52d6",
|
||||
"TEST_003_003": "a9d80a755368f677990ac389c315c0628d50e951188ea15a72ab21272a1f52d6",
|
||||
"TEST_004_001": [
|
||||
"3da1ad9438d0d6b4bd1402ea60d2a53803bb4c69ffdaa84f598a8fa14d754802",
|
||||
"c26509ac97028c2ec0b696dc321dcb4883f4b3be1e3cfaf08d58b85154f4845d",
|
||||
"4928ce9046c66de3189b47f5bbcd3cafc92f37373845787c1eb64d08b29f4a1e",
|
||||
"9d61cbb3ad289f1f8cf1eb69ee436a12416386cbede74f665357c2e58cd4d2a3",
|
||||
"37557902e5a7b5baab1fb02a9c69e48446906f491917668fc94fa47d5d76dba2"
|
||||
],
|
||||
"TEST_004_002": 5,
|
||||
"TEST_005_003": [
|
||||
"3da1ad9438d0d6b4bd1402ea60d2a53803bb4c69ffdaa84f598a8fa14d754802",
|
||||
"c26509ac97028c2ec0b696dc321dcb4883f4b3be1e3cfaf08d58b85154f4845d",
|
||||
"9d61cbb3ad289f1f8cf1eb69ee436a12416386cbede74f665357c2e58cd4d2a3"
|
||||
],
|
||||
"TEST_005_003b": 3,
|
||||
"TEST_005_004": [
|
||||
"3da1ad9438d0d6b4bd1402ea60d2a53803bb4c69ffdaa84f598a8fa14d754802",
|
||||
"c26509ac97028c2ec0b696dc321dcb4883f4b3be1e3cfaf08d58b85154f4845d",
|
||||
"9d61cbb3ad289f1f8cf1eb69ee436a12416386cbede74f665357c2e58cd4d2a3"
|
||||
],
|
||||
"TEST_005_004b": 3,
|
||||
"TEST_005_002": [
|
||||
"3da1ad9438d0d6b4bd1402ea60d2a53803bb4c69ffdaa84f598a8fa14d754802",
|
||||
"c26509ac97028c2ec0b696dc321dcb4883f4b3be1e3cfaf08d58b85154f4845d",
|
||||
"4928ce9046c66de3189b47f5bbcd3cafc92f37373845787c1eb64d08b29f4a1e",
|
||||
"9d61cbb3ad289f1f8cf1eb69ee436a12416386cbede74f665357c2e58cd4d2a3",
|
||||
"37557902e5a7b5baab1fb02a9c69e48446906f491917668fc94fa47d5d76dba2"
|
||||
],
|
||||
"TEST_005_002b": 5,
|
||||
"TEST_005_001": [
|
||||
"37557902e5a7b5baab1fb02a9c69e48446906f491917668fc94fa47d5d76dba2"
|
||||
],
|
||||
"TEST_005_001b": 1,
|
||||
"TEST_005_005": [
|
||||
"c26509ac97028c2ec0b696dc321dcb4883f4b3be1e3cfaf08d58b85154f4845d"
|
||||
],
|
||||
"TEST_005_005b": 1,
|
||||
"TEST_005_008": [
|
||||
"3da1ad9438d0d6b4bd1402ea60d2a53803bb4c69ffdaa84f598a8fa14d754802",
|
||||
"9d61cbb3ad289f1f8cf1eb69ee436a12416386cbede74f665357c2e58cd4d2a3"
|
||||
],
|
||||
"TEST_005_008b": 2,
|
||||
"TEST_005_007": [
|
||||
"c26509ac97028c2ec0b696dc321dcb4883f4b3be1e3cfaf08d58b85154f4845d"
|
||||
],
|
||||
"TEST_005_007b": 1,
|
||||
"TEST_005_006": [
|
||||
"c26509ac97028c2ec0b696dc321dcb4883f4b3be1e3cfaf08d58b85154f4845d"
|
||||
],
|
||||
"TEST_005_006b": 1,
|
||||
"TEST_005_009": [
|
||||
"3da1ad9438d0d6b4bd1402ea60d2a53803bb4c69ffdaa84f598a8fa14d754802",
|
||||
"4928ce9046c66de3189b47f5bbcd3cafc92f37373845787c1eb64d08b29f4a1e"
|
||||
],
|
||||
"TEST_005_009b": 2,
|
||||
"TEST_005_011": [
|
||||
"3da1ad9438d0d6b4bd1402ea60d2a53803bb4c69ffdaa84f598a8fa14d754802",
|
||||
"4928ce9046c66de3189b47f5bbcd3cafc92f37373845787c1eb64d08b29f4a1e"
|
||||
],
|
||||
"TEST_005_011b": 2,
|
||||
"TEST_005_010": [
|
||||
"3da1ad9438d0d6b4bd1402ea60d2a53803bb4c69ffdaa84f598a8fa14d754802"
|
||||
],
|
||||
"TEST_005_010b": 1
|
||||
}
|
||||
1465
tests/data/graphs/test-harness-assets.json
Normal file
1465
tests/data/graphs/test-harness-assets.json
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 77 B |
Binary file not shown.
|
After Width: | Height: | Size: 75 B |
Binary file not shown.
|
After Width: | Height: | Size: 77 B |
Binary file not shown.
|
After Width: | Height: | Size: 76 B |
Binary file not shown.
|
After Width: | Height: | Size: 76 B |
152
tests/data/scenes/talemate-laboratory/assets/library.json
Normal file
152
tests/data/scenes/talemate-laboratory/assets/library.json
Normal file
@@ -0,0 +1,152 @@
|
||||
{
|
||||
"assets": {
|
||||
"3da1ad9438d0d6b4bd1402ea60d2a53803bb4c69ffdaa84f598a8fa14d754802": {
|
||||
"id": "3da1ad9438d0d6b4bd1402ea60d2a53803bb4c69ffdaa84f598a8fa14d754802",
|
||||
"file_type": "png",
|
||||
"media_type": "image/png",
|
||||
"meta": {
|
||||
"name": "asset1",
|
||||
"vis_type": "SCENE_BACKGROUND",
|
||||
"gen_type": "TEXT_TO_IMAGE",
|
||||
"character_name": null,
|
||||
"prompt": "A futuristic laboratory for AI testing, in the style of digital concept art",
|
||||
"negative_prompt": "",
|
||||
"format": "SQUARE",
|
||||
"resolution": {
|
||||
"width": 1024,
|
||||
"height": 1024
|
||||
},
|
||||
"sampler_settings": {
|
||||
"steps": 40
|
||||
},
|
||||
"reference_assets": [],
|
||||
"tags": [],
|
||||
"reference": [
|
||||
"SCENE_BACKGROUND"
|
||||
],
|
||||
"analysis": null
|
||||
}
|
||||
},
|
||||
"c26509ac97028c2ec0b696dc321dcb4883f4b3be1e3cfaf08d58b85154f4845d": {
|
||||
"id": "c26509ac97028c2ec0b696dc321dcb4883f4b3be1e3cfaf08d58b85154f4845d",
|
||||
"file_type": "png",
|
||||
"media_type": "image/png",
|
||||
"meta": {
|
||||
"name": "asset2",
|
||||
"vis_type": "SCENE_BACKGROUND",
|
||||
"gen_type": "TEXT_TO_IMAGE",
|
||||
"character_name": null,
|
||||
"prompt": "A futuristic laboratory for AI testing, in the style of digital concept art",
|
||||
"negative_prompt": "",
|
||||
"format": "SQUARE",
|
||||
"resolution": {
|
||||
"width": 1024,
|
||||
"height": 1024
|
||||
},
|
||||
"sampler_settings": {
|
||||
"steps": 40
|
||||
},
|
||||
"reference_assets": [],
|
||||
"tags": [
|
||||
"mountains",
|
||||
"super intelligence"
|
||||
],
|
||||
"reference": [
|
||||
"CHARACTER_CARD"
|
||||
],
|
||||
"analysis": null
|
||||
}
|
||||
},
|
||||
"4928ce9046c66de3189b47f5bbcd3cafc92f37373845787c1eb64d08b29f4a1e": {
|
||||
"id": "4928ce9046c66de3189b47f5bbcd3cafc92f37373845787c1eb64d08b29f4a1e",
|
||||
"file_type": "png",
|
||||
"media_type": "image/png",
|
||||
"meta": {
|
||||
"name": "asset3",
|
||||
"vis_type": "SCENE_ILLUSTRATION",
|
||||
"gen_type": "TEXT_TO_IMAGE",
|
||||
"character_name": null,
|
||||
"prompt": "A futuristic laboratory for AI testing, in the style of digital concept art",
|
||||
"negative_prompt": "",
|
||||
"format": "SQUARE",
|
||||
"resolution": {
|
||||
"width": 1024,
|
||||
"height": 1024
|
||||
},
|
||||
"sampler_settings": {
|
||||
"steps": 40
|
||||
},
|
||||
"reference_assets": [],
|
||||
"tags": [
|
||||
"super intelligence"
|
||||
],
|
||||
"reference": [
|
||||
"SCENE_BACKGROUND",
|
||||
"SCENE_ILLUSTRATION",
|
||||
"SCENE_CARD"
|
||||
],
|
||||
"analysis": null
|
||||
}
|
||||
},
|
||||
"9d61cbb3ad289f1f8cf1eb69ee436a12416386cbede74f665357c2e58cd4d2a3": {
|
||||
"id": "9d61cbb3ad289f1f8cf1eb69ee436a12416386cbede74f665357c2e58cd4d2a3",
|
||||
"file_type": "png",
|
||||
"media_type": "image/png",
|
||||
"meta": {
|
||||
"name": "asset4",
|
||||
"vis_type": "SCENE_BACKGROUND",
|
||||
"gen_type": "TEXT_TO_IMAGE",
|
||||
"character_name": null,
|
||||
"prompt": "A futuristic laboratory for AI testing, in the style of digital concept art",
|
||||
"negative_prompt": "",
|
||||
"format": "SQUARE",
|
||||
"resolution": {
|
||||
"width": 1024,
|
||||
"height": 1024
|
||||
},
|
||||
"sampler_settings": {
|
||||
"steps": 40
|
||||
},
|
||||
"reference_assets": [],
|
||||
"tags": [
|
||||
"city",
|
||||
"holograms"
|
||||
],
|
||||
"reference": [],
|
||||
"analysis": null
|
||||
}
|
||||
},
|
||||
"37557902e5a7b5baab1fb02a9c69e48446906f491917668fc94fa47d5d76dba2": {
|
||||
"id": "37557902e5a7b5baab1fb02a9c69e48446906f491917668fc94fa47d5d76dba2",
|
||||
"file_type": "png",
|
||||
"media_type": "image/png",
|
||||
"meta": {
|
||||
"name": "asset_5",
|
||||
"vis_type": "CHARACTER_CARD",
|
||||
"gen_type": "IMAGE_EDIT",
|
||||
"character_name": "Sebastian",
|
||||
"prompt": "A futuristic character portrait of super intellience",
|
||||
"negative_prompt": "",
|
||||
"format": "PORTRAIT",
|
||||
"resolution": {
|
||||
"width": 768,
|
||||
"height": 1344
|
||||
},
|
||||
"sampler_settings": {
|
||||
"steps": 40
|
||||
},
|
||||
"reference_assets": [
|
||||
"c26509ac97028c2ec0b696dc321dcb4883f4b3be1e3cfaf08d58b85154f4845d"
|
||||
],
|
||||
"tags": [
|
||||
"ai"
|
||||
],
|
||||
"reference": [
|
||||
"CHARACTER_CARD",
|
||||
"CHARACTER_PORTRAIT"
|
||||
],
|
||||
"analysis": null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
152
tests/data/scenes/talemate-laboratory/talemate-lab.json
Normal file
152
tests/data/scenes/talemate-laboratory/talemate-lab.json
Normal file
@@ -0,0 +1,152 @@
|
||||
{
|
||||
"id": "test-assets",
|
||||
"name": "Test Assets Scene",
|
||||
"assets": {
|
||||
"cover_image": null,
|
||||
"assets": {
|
||||
"3da1ad9438d0d6b4bd1402ea60d2a53803bb4c69ffdaa84f598a8fa14d754802": {
|
||||
"id": "3da1ad9438d0d6b4bd1402ea60d2a53803bb4c69ffdaa84f598a8fa14d754802",
|
||||
"file_type": "png",
|
||||
"media_type": "image/png",
|
||||
"meta": {
|
||||
"name": "asset1",
|
||||
"vis_type": "SCENE_BACKGROUND",
|
||||
"gen_type": "TEXT_TO_IMAGE",
|
||||
"character_name": null,
|
||||
"prompt": "A futuristic laboratory for AI testing, in the style of digital concept art",
|
||||
"negative_prompt": "",
|
||||
"format": "SQUARE",
|
||||
"resolution": {
|
||||
"width": 1024,
|
||||
"height": 1024
|
||||
},
|
||||
"sampler_settings": {
|
||||
"steps": 40
|
||||
},
|
||||
"reference_assets": [],
|
||||
"tags": [],
|
||||
"reference": [
|
||||
"SCENE_BACKGROUND"
|
||||
]
|
||||
}
|
||||
},
|
||||
"c26509ac97028c2ec0b696dc321dcb4883f4b3be1e3cfaf08d58b85154f4845d": {
|
||||
"id": "c26509ac97028c2ec0b696dc321dcb4883f4b3be1e3cfaf08d58b85154f4845d",
|
||||
"file_type": "png",
|
||||
"media_type": "image/png",
|
||||
"meta": {
|
||||
"name": "asset2",
|
||||
"vis_type": "SCENE_BACKGROUND",
|
||||
"gen_type": "TEXT_TO_IMAGE",
|
||||
"character_name": null,
|
||||
"prompt": "A futuristic laboratory for AI testing, in the style of digital concept art",
|
||||
"negative_prompt": "",
|
||||
"format": "SQUARE",
|
||||
"resolution": {
|
||||
"width": 1024,
|
||||
"height": 1024
|
||||
},
|
||||
"sampler_settings": {
|
||||
"steps": 40
|
||||
},
|
||||
"reference_assets": [],
|
||||
"tags": [
|
||||
"mountains",
|
||||
"super intelligence"
|
||||
],
|
||||
"reference": [
|
||||
"CHARACTER_CARD"
|
||||
]
|
||||
}
|
||||
},
|
||||
"4928ce9046c66de3189b47f5bbcd3cafc92f37373845787c1eb64d08b29f4a1e": {
|
||||
"id": "4928ce9046c66de3189b47f5bbcd3cafc92f37373845787c1eb64d08b29f4a1e",
|
||||
"file_type": "png",
|
||||
"media_type": "image/png",
|
||||
"meta": {
|
||||
"name": "asset3",
|
||||
"vis_type": "SCENE_ILLUSTRATION",
|
||||
"gen_type": "TEXT_TO_IMAGE",
|
||||
"character_name": null,
|
||||
"prompt": "A futuristic laboratory for AI testing, in the style of digital concept art",
|
||||
"negative_prompt": "",
|
||||
"format": "SQUARE",
|
||||
"resolution": {
|
||||
"width": 1024,
|
||||
"height": 1024
|
||||
},
|
||||
"sampler_settings": {
|
||||
"steps": 40
|
||||
},
|
||||
"reference_assets": [],
|
||||
"tags": [
|
||||
"super intelligence"
|
||||
],
|
||||
"reference": [
|
||||
"SCENE_BACKGROUND",
|
||||
"SCENE_ILLUSTRATION",
|
||||
"SCENE_CARD"
|
||||
]
|
||||
}
|
||||
},
|
||||
"9d61cbb3ad289f1f8cf1eb69ee436a12416386cbede74f665357c2e58cd4d2a3": {
|
||||
"id": "9d61cbb3ad289f1f8cf1eb69ee436a12416386cbede74f665357c2e58cd4d2a3",
|
||||
"file_type": "png",
|
||||
"media_type": "image/png",
|
||||
"meta": {
|
||||
"name": "asset4",
|
||||
"vis_type": "SCENE_BACKGROUND",
|
||||
"gen_type": "TEXT_TO_IMAGE",
|
||||
"character_name": null,
|
||||
"prompt": "A futuristic laboratory for AI testing, in the style of digital concept art",
|
||||
"negative_prompt": "",
|
||||
"format": "SQUARE",
|
||||
"resolution": {
|
||||
"width": 1024,
|
||||
"height": 1024
|
||||
},
|
||||
"sampler_settings": {
|
||||
"steps": 40
|
||||
},
|
||||
"reference_assets": [],
|
||||
"tags": [
|
||||
"city",
|
||||
"holograms"
|
||||
],
|
||||
"reference": []
|
||||
}
|
||||
},
|
||||
"37557902e5a7b5baab1fb02a9c69e48446906f491917668fc94fa47d5d76dba2": {
|
||||
"id": "37557902e5a7b5baab1fb02a9c69e48446906f491917668fc94fa47d5d76dba2",
|
||||
"file_type": "png",
|
||||
"media_type": "image/png",
|
||||
"meta": {
|
||||
"name": "asset_5",
|
||||
"vis_type": "CHARACTER_CARD",
|
||||
"gen_type": "IMAGE_EDIT",
|
||||
"character_name": "Sebastian",
|
||||
"prompt": "A futuristic character portrait of super intellience",
|
||||
"negative_prompt": "",
|
||||
"format": "PORTRAIT",
|
||||
"resolution": {
|
||||
"width": 768,
|
||||
"height": 1344
|
||||
},
|
||||
"sampler_settings": {
|
||||
"steps": 40
|
||||
},
|
||||
"reference_assets": [
|
||||
"c26509ac97028c2ec0b696dc321dcb4883f4b3be1e3cfaf08d58b85154f4845d"
|
||||
],
|
||||
"tags": [
|
||||
"ai"
|
||||
],
|
||||
"reference": [
|
||||
"CHARACTER_CARD",
|
||||
"CHARACTER_PORTRAIT"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
303
tests/test_character_card_import.py
Normal file
303
tests/test_character_card_import.py
Normal file
@@ -0,0 +1,303 @@
|
||||
from talemate.load.character_card import (
|
||||
create_manual_context_from_character_book,
|
||||
CharacterBook,
|
||||
CharacterBookEntry,
|
||||
)
|
||||
from talemate.world_state import ManualContext
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_create_manual_context_from_character_book_none():
|
||||
"""Test that None character_book returns empty dict."""
|
||||
result = create_manual_context_from_character_book(None)
|
||||
assert result == {}
|
||||
|
||||
|
||||
def test_create_manual_context_from_character_book_empty():
|
||||
"""Test that empty character_book returns empty dict."""
|
||||
character_book = CharacterBook(entries=[])
|
||||
result = create_manual_context_from_character_book(character_book)
|
||||
assert result == {}
|
||||
|
||||
|
||||
def test_create_manual_context_from_character_book_single_entry():
|
||||
"""Test creating ManualContext from a single character book entry."""
|
||||
entry = CharacterBookEntry(
|
||||
keys=["Alice", "Character"],
|
||||
content="Alice is a friendly character.",
|
||||
enabled=True,
|
||||
id=1,
|
||||
)
|
||||
character_book = CharacterBook(
|
||||
name="Test Book",
|
||||
entries=[entry],
|
||||
)
|
||||
|
||||
result = create_manual_context_from_character_book(character_book)
|
||||
|
||||
assert len(result) == 1
|
||||
entry_id = "character_book_1"
|
||||
assert entry_id in result
|
||||
|
||||
manual_context = result[entry_id]
|
||||
assert isinstance(manual_context, ManualContext)
|
||||
assert manual_context.id == entry_id
|
||||
assert manual_context.text == "Alice is a friendly character."
|
||||
assert manual_context.meta["source"] == "imported"
|
||||
assert "chara" in manual_context.meta
|
||||
assert manual_context.meta["chara"]["character_book_name"] == "Test Book"
|
||||
assert manual_context.meta["chara"]["keys"] == ["Alice", "Character"]
|
||||
assert manual_context.shared is False
|
||||
|
||||
|
||||
def test_create_manual_context_from_character_book_multiple_entries():
|
||||
"""Test creating ManualContext from multiple character book entries."""
|
||||
entries = [
|
||||
CharacterBookEntry(
|
||||
keys=["Alice"],
|
||||
content="Alice is a character.",
|
||||
enabled=True,
|
||||
id=1,
|
||||
),
|
||||
CharacterBookEntry(
|
||||
keys=["Bob"],
|
||||
content="Bob is another character.",
|
||||
enabled=True,
|
||||
id=2,
|
||||
),
|
||||
]
|
||||
character_book = CharacterBook(
|
||||
name="Multi Entry Book",
|
||||
entries=entries,
|
||||
)
|
||||
|
||||
result = create_manual_context_from_character_book(character_book)
|
||||
|
||||
assert len(result) == 2
|
||||
assert "character_book_1" in result
|
||||
assert "character_book_2" in result
|
||||
|
||||
assert result["character_book_1"].text == "Alice is a character."
|
||||
assert result["character_book_2"].text == "Bob is another character."
|
||||
|
||||
|
||||
def test_create_manual_context_from_character_book_disabled_entry():
|
||||
"""Test that disabled entries are skipped by default."""
|
||||
entries = [
|
||||
CharacterBookEntry(
|
||||
keys=["Alice"],
|
||||
content="Alice is enabled.",
|
||||
enabled=True,
|
||||
id=1,
|
||||
),
|
||||
CharacterBookEntry(
|
||||
keys=["Bob"],
|
||||
content="Bob is disabled.",
|
||||
enabled=False,
|
||||
id=2,
|
||||
),
|
||||
]
|
||||
character_book = CharacterBook(entries=entries)
|
||||
|
||||
result = create_manual_context_from_character_book(character_book)
|
||||
|
||||
assert len(result) == 1
|
||||
assert "character_book_1" in result
|
||||
assert "character_book_2" not in result
|
||||
|
||||
|
||||
def test_create_manual_context_from_character_book_include_disabled():
|
||||
"""Test that disabled entries can be included when skip_disabled=False."""
|
||||
entries = [
|
||||
CharacterBookEntry(
|
||||
keys=["Alice"],
|
||||
content="Alice is enabled.",
|
||||
enabled=True,
|
||||
id=1,
|
||||
),
|
||||
CharacterBookEntry(
|
||||
keys=["Bob"],
|
||||
content="Bob is disabled.",
|
||||
enabled=False,
|
||||
id=2,
|
||||
),
|
||||
]
|
||||
character_book = CharacterBook(entries=entries)
|
||||
|
||||
result = create_manual_context_from_character_book(
|
||||
character_book, skip_disabled=False
|
||||
)
|
||||
|
||||
assert len(result) == 2
|
||||
assert "character_book_1" in result
|
||||
assert "character_book_2" in result
|
||||
|
||||
|
||||
def test_create_manual_context_from_character_book_with_optional_fields():
|
||||
"""Test creating ManualContext with optional fields populated."""
|
||||
entry = CharacterBookEntry(
|
||||
keys=["Alice"],
|
||||
content="Alice content",
|
||||
enabled=True,
|
||||
id=1,
|
||||
name="Alice Entry",
|
||||
priority=10,
|
||||
selective=True,
|
||||
secondary_keys=["secondary1", "secondary2"],
|
||||
constant=True,
|
||||
position="before_char",
|
||||
insertion_order=5,
|
||||
case_sensitive=True,
|
||||
)
|
||||
character_book = CharacterBook(
|
||||
name="Optional Fields Book",
|
||||
entries=[entry],
|
||||
)
|
||||
|
||||
result = create_manual_context_from_character_book(character_book)
|
||||
|
||||
assert len(result) == 1
|
||||
# Entry name should be used as ID when set
|
||||
manual_context = result["Alice Entry"]
|
||||
assert manual_context.id == "Alice Entry"
|
||||
meta = manual_context.meta
|
||||
chara = meta["chara"]
|
||||
|
||||
assert chara["entry_name"] == "Alice Entry"
|
||||
assert chara["priority"] == 10
|
||||
assert chara["selective"] is True
|
||||
assert chara["secondary_keys"] == ["secondary1", "secondary2"]
|
||||
assert chara["constant"] is True
|
||||
assert chara["position"] == "before_char"
|
||||
assert chara["insertion_order"] == 5
|
||||
assert chara["case_sensitive"] is True
|
||||
|
||||
|
||||
def test_create_manual_context_from_character_book_with_extensions():
|
||||
"""Test that extensions are included in chara metadata."""
|
||||
entry = CharacterBookEntry(
|
||||
keys=["Alice"],
|
||||
content="Alice content",
|
||||
enabled=True,
|
||||
id=1,
|
||||
extensions={"custom_field": "custom_value", "depth": 4},
|
||||
)
|
||||
character_book = CharacterBook(
|
||||
name="Extensions Book",
|
||||
entries=[entry],
|
||||
extensions={"book_extension": "book_value"},
|
||||
)
|
||||
|
||||
result = create_manual_context_from_character_book(character_book)
|
||||
|
||||
assert len(result) == 1
|
||||
manual_context = result["character_book_1"]
|
||||
assert "extensions" in manual_context.meta["chara"]
|
||||
assert manual_context.meta["chara"]["extensions"]["custom_field"] == "custom_value"
|
||||
assert manual_context.meta["chara"]["extensions"]["depth"] == 4
|
||||
|
||||
|
||||
def test_create_manual_context_from_character_book_without_id():
|
||||
"""Test that entries without id get UUID-based IDs."""
|
||||
entry = CharacterBookEntry(
|
||||
keys=["Alice", "Character"],
|
||||
content="Alice is a character.",
|
||||
enabled=True,
|
||||
# No id provided
|
||||
)
|
||||
character_book = CharacterBook(entries=[entry])
|
||||
|
||||
result = create_manual_context_from_character_book(character_book)
|
||||
|
||||
assert len(result) == 1
|
||||
# Should have a generated UUID-based ID
|
||||
entry_id = list(result.keys())[0]
|
||||
assert entry_id.startswith("character_book_")
|
||||
# UUID hex is 10 chars after the prefix
|
||||
assert len(entry_id) == len("character_book_") + 10
|
||||
|
||||
|
||||
def test_create_manual_context_from_character_book_dict_input():
|
||||
"""Test that dict input is converted to CharacterBook."""
|
||||
character_book_dict = {
|
||||
"name": "Dict Book",
|
||||
"entries": [
|
||||
{
|
||||
"keys": ["Alice"],
|
||||
"content": "Alice from dict.",
|
||||
"enabled": True,
|
||||
"id": 1,
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
result = create_manual_context_from_character_book(character_book_dict)
|
||||
|
||||
assert len(result) == 1
|
||||
assert "character_book_1" in result
|
||||
assert result["character_book_1"].text == "Alice from dict."
|
||||
assert (
|
||||
result["character_book_1"].meta["chara"]["character_book_name"] == "Dict Book"
|
||||
)
|
||||
|
||||
|
||||
def test_create_manual_context_from_character_book_invalid_dict():
|
||||
"""Test that invalid dict input returns empty dict."""
|
||||
invalid_dict = {
|
||||
"name": "Invalid",
|
||||
"entries": [
|
||||
{
|
||||
# Missing required 'keys' field
|
||||
"content": "Invalid entry.",
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
result = create_manual_context_from_character_book(invalid_dict)
|
||||
|
||||
# Should return empty dict due to validation error
|
||||
assert result == {}
|
||||
|
||||
|
||||
def test_create_manual_context_from_character_book_no_book_name():
|
||||
"""Test handling of missing book name."""
|
||||
entry = CharacterBookEntry(
|
||||
keys=["Alice"],
|
||||
content="Alice content",
|
||||
enabled=True,
|
||||
id=1,
|
||||
)
|
||||
character_book = CharacterBook(entries=[entry]) # No name
|
||||
|
||||
result = create_manual_context_from_character_book(character_book)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result["character_book_1"].meta["chara"]["character_book_name"] == ""
|
||||
|
||||
|
||||
def test_create_manual_context_from_character_book_without_meta():
|
||||
"""Test that chara metadata is not included when import_meta=False."""
|
||||
entry = CharacterBookEntry(
|
||||
keys=["Alice"],
|
||||
content="Alice content",
|
||||
enabled=True,
|
||||
id=1,
|
||||
priority=10,
|
||||
)
|
||||
character_book = CharacterBook(
|
||||
name="Test Book",
|
||||
entries=[entry],
|
||||
)
|
||||
|
||||
result = create_manual_context_from_character_book(
|
||||
character_book, import_meta=False
|
||||
)
|
||||
|
||||
assert len(result) == 1
|
||||
manual_context = result["character_book_1"]
|
||||
assert manual_context.meta["source"] == "imported"
|
||||
assert "chara" not in manual_context.meta
|
||||
@@ -3,6 +3,7 @@ import json
|
||||
import pytest
|
||||
import contextvars
|
||||
import talemate.agents as agents
|
||||
import pydantic
|
||||
import talemate.game.engine.nodes.load_definitions # noqa: F401
|
||||
import talemate.agents.director # noqa: F401
|
||||
import talemate.agents.memory
|
||||
@@ -123,6 +124,38 @@ def mock_scene():
|
||||
return scene
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_scene_with_assets():
|
||||
scene = MockScene()
|
||||
bootstrap_scene(scene)
|
||||
|
||||
# Load test assets from the test scene file
|
||||
test_scene_path = os.path.join(
|
||||
BASE_DIR, "data", "scenes", "talemate-laboratory", "talemate-lab.json"
|
||||
)
|
||||
with open(test_scene_path, "r") as f:
|
||||
test_scene_data = json.load(f)
|
||||
|
||||
# Override scenes_dir to point to test data directory
|
||||
test_scenes_dir = os.path.join(BASE_DIR, "data", "scenes")
|
||||
scene.scenes_dir = lambda: test_scenes_dir
|
||||
scene.project_name = "talemate-laboratory"
|
||||
|
||||
# Create library.json file with assets from the scene file
|
||||
if "assets" in test_scene_data and "assets" in test_scene_data["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
|
||||
|
||||
|
||||
def bootstrap_scene(mock_scene):
|
||||
bootstrap_engine()
|
||||
client = MockClient("test_client")
|
||||
@@ -147,18 +180,38 @@ def bootstrap_scene(mock_scene):
|
||||
}
|
||||
|
||||
|
||||
def serialize_state(obj):
|
||||
"""Custom JSON serializer for Pydantic models"""
|
||||
if isinstance(obj, pydantic.BaseModel):
|
||||
return obj.model_dump()
|
||||
raise TypeError(f"Object of type {type(obj)} is not JSON serializable")
|
||||
|
||||
|
||||
def normalize_state(data):
|
||||
"""Convert Pydantic models to dicts for comparison"""
|
||||
if isinstance(data, pydantic.BaseModel):
|
||||
return data.model_dump()
|
||||
elif isinstance(data, dict):
|
||||
return {k: normalize_state(v) for k, v in data.items()}
|
||||
elif isinstance(data, list):
|
||||
return [normalize_state(item) for item in data]
|
||||
return data
|
||||
|
||||
|
||||
def make_assert_fn(name: str, write_results: bool = False):
|
||||
async def assert_fn(state: GraphState):
|
||||
if write_results or not os.path.exists(
|
||||
os.path.join(RESULTS_DIR, f"{name}.json")
|
||||
):
|
||||
with open(os.path.join(RESULTS_DIR, f"{name}.json"), "w") as f:
|
||||
json.dump(state.shared, f, indent=4)
|
||||
json.dump(state.shared, f, indent=4, default=serialize_state)
|
||||
else:
|
||||
with open(os.path.join(RESULTS_DIR, f"{name}.json"), "r") as f:
|
||||
expected = json.load(f)
|
||||
|
||||
assert state.shared == expected
|
||||
# Normalize state.shared to convert Pydantic models to dicts for comparison
|
||||
normalized_shared = normalize_state(state.shared)
|
||||
assert normalized_shared == expected
|
||||
|
||||
return assert_fn
|
||||
|
||||
@@ -230,3 +283,9 @@ async def test_graph_collectors(mock_scene):
|
||||
async def test_graph_context_ids(mock_scene):
|
||||
fn = make_graph_test("test-harness-context-ids", False)
|
||||
await fn(mock_scene)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_graph_assets(mock_scene_with_assets):
|
||||
fn = make_graph_test("test-harness-assets", False)
|
||||
await fn(mock_scene_with_assets)
|
||||
|
||||
Reference in New Issue
Block a user