Files
talemate/tests/conftest.py
veguAI 42a8863e65 0.36.0 (#255)
Major Features

- API key encryption at rest using Fernet (OS keyring with file fallback)
- Prompt Manager: unified UI with template groups, priority ordering, override tracking, response extractors
- Scene context history review panel with token budgets and best-fit mode
- Multiple concurrent director chats with auto-generated titles
- Granular scene state reset dialog
- Time passage insert/edit/delete in scene view
- Image analysis via OpenAI-compatible and Talemate Client backends
- Volatile context placement after scene history for improved prompt caching

Improvements

- Configurable narrator generation length per narration type
- AI Aware conversation mode
- Summarizer: custom instructions, writing style inclusion, short line filtering
- Anthropic: adaptive thinking support, updated model list (opus-4-5/4-6, haiku-4-5)
- Google: gemini-3.1 support
- World editor: generate from topic, quick create state reinforcement, reorganized menus
- Node editor: promote scene modules to global
- Frontend: version mismatch detection, hideable bracket content, required scene name
- TTS: improved pause handling, audio tag support for vocal markers (ElevenLabs v3)
- Writing style template for AI-generated instructions
- Added Kimi.jinja2 LLM prompt template
- Option to disable character names in stopping strings
- Client response length enforcement options
- Graduated token count sliders
- Increased summarizer token threshold max

Bugfixes

- Fix bracket/paren/brace terminators stripped from message ends
- Fix colon in conversation causing content loss
- Fix "Use as reference" navigating to blank page
- Fix avatar regeneration and manual regenerate
- Fix conversation agent ignoring generation length
- Fix duplicate length instructions with reasoning enabled
- Fix trailing newline on message edits
- Fix summarize dialogue sending too much context with layered history
- Fix layered history inspection and construction issues
- Fix empty response handling in summarization
- Fix context ID dot notation with dotted character names
- Fix recursive retry in focal agent
- Fix leading whitespace causing duplicate prepared responses
- Fix summarization not stripping ANALYSIS OF lines
- Fix template group selection/removal in prompt manager
- Fix multiline text in parentheses/brackets parser
- Fix determine_character_name resolution
- Fix character activate/deactivate desyncing creative menu
- Fix character image generation missing context
- Fix LMStudio client not sending token limits
- Fix Recent Scene images on newer Chromium
- Fix sequential reinforcement messages cut off at first linebreak
- Fix reinforcement removal not clearing state
- Fixes #252, #256, #258

Deprecations

- Removed context investigations (replaced by AI-assisted RAG mixin)
- Removed deprecated prompt templates (fix-continuity-errors, fix-exposition, etc.)
- Removed conversation/edit.jinja2, auto break repetition, CLI reset layered history
---------

Co-authored-by: theDTV2 <47825738+theDTV2@users.noreply.github.com>
2026-03-15 12:00:57 +02:00

172 lines
5.0 KiB
Python

"""
Shared pytest fixtures and test infrastructure.
Provides MockClient, MockScene, and bootstrap functions used across
multiple test modules (test_graphs, test_layered_history, etc.).
"""
import contextvars
from collections import deque
from pathlib import Path
import pytest
import yaml
import talemate.agents as agents
import talemate.agents.memory
import talemate.agents.tts.voice_library as voice_library
import talemate.config.state as config_state
import talemate.instance as instance
from talemate.client import ClientBase
from talemate.config.schema import Config
from talemate.tale_mate import Scene
# Root of the repository (where config.example.yaml lives)
_REPO_ROOT = Path(__file__).resolve().parent.parent
@pytest.fixture(autouse=True, scope="session")
def _use_example_config():
"""Ensure all tests use config.example.yaml instead of the local config.yaml.
This prevents local configuration from leaking into test results and
keeps CI and local runs deterministic.
"""
example_path = _REPO_ROOT / "config.example.yaml"
with open(example_path, "r") as f:
yaml_data = yaml.safe_load(f) or {}
test_config = Config.model_validate(yaml_data)
original = config_state.CONFIG
config_state.CONFIG = test_config
yield
config_state.CONFIG = original
# ---------------------------------------------------------------------------
# Contextvar-based response queue for MockClient
# ---------------------------------------------------------------------------
client_responses = contextvars.ContextVar("client_responses", default=deque())
class MockClientContext:
"""Async context manager that provides a fresh response queue."""
async def __aenter__(self):
try:
self.client_responses = client_responses.get()
except LookupError:
_client_responses = deque()
self.token = client_responses.set(_client_responses)
self.client_responses = _client_responses
return self.client_responses
async def __aexit__(self, exc_type, exc_value, traceback):
if hasattr(self, "token"):
client_responses.reset(self.token)
# ---------------------------------------------------------------------------
# Mock classes
# ---------------------------------------------------------------------------
class MockClient(ClientBase):
"""LLM client stub that pops pre-defined responses from a queue."""
def __init__(self, name: str):
self.name = name
self.remote_model_name = "test-model"
self.current_status = "idle"
self.prompt_history = []
@property
def enabled(self):
return True
async def send_prompt(
self, prompt, kind="conversation", finalize=lambda x: x, retries=2, **kwargs
):
response_stack = client_responses.get()
self.prompt_history.append({"prompt": prompt, "kind": kind})
if not response_stack:
return ""
return response_stack.popleft()
class MockMemoryAgent(talemate.agents.memory.MemoryAgent):
"""MemoryAgent with no-op persistence methods."""
async def add_many(self, items: list[dict]):
pass
async def delete(self, filters: dict):
pass
class MockScene(Scene):
"""Real Scene subclass with auto_progress forced on."""
@property
def auto_progress(self):
return True
# ---------------------------------------------------------------------------
# Bootstrap helpers
# ---------------------------------------------------------------------------
def bootstrap_engine():
"""Instantiate all real agents (using MockMemoryAgent for memory)."""
voice_library.VOICE_LIBRARY = voice_library.VoiceLibrary(voices={})
for agent_type in agents.AGENT_CLASSES:
if agent_type == "memory":
agent = MockMemoryAgent()
else:
agent = agents.AGENT_CLASSES[agent_type]()
instance.AGENTS[agent_type] = agent
def pytest_addoption(parser):
"""Add custom command-line options."""
parser.addoption(
"--update-baselines",
action="store_true",
default=False,
help="Update baseline snapshot files instead of comparing against them.",
)
@pytest.fixture
def update_baselines(request):
"""Whether to update baseline files instead of comparing."""
return request.config.getoption("--update-baselines")
def bootstrap_scene(mock_scene):
"""Wire a MockClient and the mock_scene into every agent."""
bootstrap_engine()
client = MockClient("test_client")
for agent in instance.AGENTS.values():
agent.client = client
agent.scene = mock_scene
director = instance.get_agent("director")
conversation = instance.get_agent("conversation")
summarizer = instance.get_agent("summarizer")
editor = instance.get_agent("editor")
world_state = instance.get_agent("world_state")
mock_scene.mock_client = client
return {
"director": director,
"conversation": conversation,
"summarizer": summarizer,
"editor": editor,
"world_state": world_state,
}