"""Integration tests demonstrating NPC chat history and serialization in action.""" import json import tempfile import os import asyncio from pathlib import Path from gumyum_npc_client import GumYumClient, NPC from gumyum_npc_sync import GumYumClient as SyncGumYumClient, NPC as SyncNPC async def test_async_npc_conversation_flow(): """Test a complete conversation flow with history tracking.""" # This would use real API in production # For testing, we'd mock the client responses # Example flow: async with GumYumClient(api_key="test-key", api_secret="test-secret") as client: # Spawn an NPC npc = await client.npcs.spawn( universe_id="blade-runner", seed=42, npc_id=123456789 ) # First interaction response1 = await npc.chat_with_history("Tell me about this city") print(f"{npc.name}: {response1.choices[0].message.content}") # Continue conversation - history is automatically included response2 = await npc.chat_with_history("What's the most dangerous part?") print(f"{npc.name}: {response2.choices[0].message.content}") # Save the conversation with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: f.write(npc.to_json()) saved_file = f.name # Later... load and continue with open(saved_file, "r") as f: loaded_npc = NPC.from_json(f.read(), client) # Continue from where we left off response3 = await loaded_npc.chat_with_history("How can I stay safe there?") print(f"{loaded_npc.name}: {response3.choices[0].message.content}") # Clean up os.unlink(saved_file) def test_sync_npc_game_save_example(): """Example of saving/loading NPCs in a game save system.""" class GameSaveSystem: def __init__(self, save_dir): self.save_dir = Path(save_dir) self.save_dir.mkdir(exist_ok=True) def save_npc(self, npc: SyncNPC, slot: str): """Save an NPC to a save slot.""" save_path = self.save_dir / f"npc_{slot}_{npc.npc_id}.json" with open(save_path, "w") as f: f.write(npc.to_json()) return save_path def load_npc(self, slot: str, npc_id: int, client) -> SyncNPC: """Load an NPC from a save slot.""" save_path = self.save_dir / f"npc_{slot}_{npc_id}.json" if not save_path.exists(): return None with open(save_path, "r") as f: return SyncNPC.from_json(f.read(), client) def save_all_npcs(self, npcs: list, slot: str): """Save all active NPCs.""" manifest = {"slot": slot, "npcs": []} for npc in npcs: path = self.save_npc(npc, slot) manifest["npcs"].append( {"npc_id": npc.npc_id, "name": npc.name, "path": str(path.name)} ) # Save manifest manifest_path = self.save_dir / f"manifest_{slot}.json" with open(manifest_path, "w") as f: json.dump(manifest, f, indent=2) return manifest def load_all_npcs(self, slot: str, client) -> list: """Load all NPCs from a save slot.""" manifest_path = self.save_dir / f"manifest_{slot}.json" if not manifest_path.exists(): return [] with open(manifest_path, "r") as f: manifest = json.load(f) npcs = [] for npc_info in manifest["npcs"]: npc = self.load_npc(slot, npc_info["npc_id"], client) if npc: npcs.append(npc) return npcs # Example usage with tempfile.TemporaryDirectory() as temp_dir: save_system = GameSaveSystem(temp_dir) client = SyncGumYumClient(api_key="test", api_secret="test") # Create some NPCs with conversations npcs = [] # Merchant with trade conversation merchant = SyncNPC( npc_id=1001, name="Elara", profession="merchant", personality_type=3, universe_id="fantasy", seed=42, spawned={"location": "market", "mood": "friendly", "stress_level": 2}, ) merchant.chat_history.extend( [ {"role": "user", "content": "What are you selling?"}, { "role": "assistant", "content": "I have potions, herbs, and rare artifacts!", }, {"role": "user", "content": "How much for a healing potion?"}, { "role": "assistant", "content": "50 gold pieces, but I'll give you a discount - 40 gold.", }, ] ) npcs.append(merchant) # Guard with security conversation guard = SyncNPC( npc_id=1002, name="Marcus", profession="guard", personality_type=1, universe_id="fantasy", seed=42, spawned={"location": "gate", "mood": "alert", "stress_level": 4}, ) guard.chat_history.extend( [ {"role": "user", "content": "Can I enter the city?"}, {"role": "assistant", "content": "State your business, traveler."}, {"role": "user", "content": "I'm here to trade goods."}, { "role": "assistant", "content": "Very well. Keep your weapons sheathed and cause no trouble.", }, ] ) npcs.append(guard) # Save game state manifest = save_system.save_all_npcs(npcs, "save_001") print(f"Saved {len(npcs)} NPCs to slot: save_001") # Later... load game state loaded_npcs = save_system.load_all_npcs("save_001", client) print(f"Loaded {len(loaded_npcs)} NPCs from save") # Verify conversations were preserved for npc in loaded_npcs: print(f"\n{npc.name} ({npc.profession}) - {len(npc.chat_history)} messages") if npc.chat_history: print(f" Last exchange: {npc.chat_history[-1]['content'][:50]}...") def test_streaming_with_history(): """Example of streaming responses with history tracking.""" async def streaming_conversation(): async with GumYumClient(api_key="test", api_secret="test") as client: npc = await client.npcs.spawn("cyberpunk", 42, 999888777) # Stream first response print(f"{npc.name}: ", end="", flush=True) async for chunk in await npc.chat_with_history( "Tell me your story", stream=True ): if "choices" in chunk and chunk["choices"]: delta = chunk["choices"][0].get("delta", {}) if content := delta.get("content"): print(content, end="", flush=True) print() # New line # Check history - streaming responses should still be tracked print(f"\nConversation history: {len(npc.chat_history)} messages") # Continue with another streamed message print(f"{npc.name}: ", end="", flush=True) async for chunk in await npc.chat_with_history( "What happened next?", stream=True ): if "choices" in chunk and chunk["choices"]: delta = chunk["choices"][0].get("delta", {}) if content := delta.get("content"): print(content, end="", flush=True) print() # Note: This would be run with asyncio.run(streaming_conversation()) in practice if __name__ == "__main__": # Run sync example print("=== Sync Game Save Example ===") test_sync_npc_game_save_example() print("\n=== Async Conversation Example ===") # Async example would need proper async environment # asyncio.run(test_async_npc_conversation_flow())