NPC Conversations
Create intelligent game characters with NPCClient
NPC Conversations
NPCClient is a client class designed specifically for game NPC (Non-Player Character) conversations. It features automatic history management, character design, memory systems, action triggering, and reply predictions - letting you focus on character design and dialogue logic.
When to Use NPCClient
| Feature | ChatClient | NPCClient |
|---|---|---|
| Automatic history management | No | Yes |
| Character personality | Manual | Built-in |
| Memory system | No | Yes |
| Reply predictions | No | Yes |
| Action/tool calling | Yes | Yes (simplified) |
| Save/Load conversations | Manual | Built-in |
Use NPCClient when:
- Building game NPCs with persistent personalities
- Need automatic conversation history tracking
- Want dynamic memory that affects NPC behavior
- Need suggested player responses
Use ChatClient when:
- Single Q&A without history
- Full control over message management
- Non-character AI interactions
Create NPCClient
import { PlayKitSDK } from 'playkit-sdk';
const sdk = new PlayKitSDK({
gameId: 'your-game-id',
developerToken: 'your-token'
});
await sdk.initialize();
// Create with default settings
const npc = sdk.createNPCClient();
// Create with configuration
const wizard = sdk.createNPCClient({
characterDesign: 'You are an ancient wizard who speaks in riddles.',
temperature: 0.8,
maxHistoryLength: 30
});Basic Conversation
Simple Talk
const response = await npc.talk('Hello, who are you?');
console.log('NPC:', response);
const response2 = await npc.talk('What quest do you have for me?');
console.log('NPC:', response2);
// NPC remembers the previous exchangeStreaming Responses
Display responses in real-time as they generate:
await npc.talkStream(
'Tell me about the ancient kingdom',
// Called for each text chunk
(chunk) => {
process.stdout.write(chunk);
},
// Called when complete (optional)
(fullText) => {
console.log('\n--- Response complete ---');
}
);Check Speaking Status
if (npc.isTalking) {
console.log('NPC is still responding...');
}Character Design
Character design defines the NPC's personality, background, and behavior.
Set Character Design
const npc = sdk.createNPCClient({
characterDesign: `You are Eldric, a mysterious wizard.
Background: You have lived for 300 years in the Enchanted Forest.
Personality: Wise but cryptic, often speaking in riddles.
Goal: Guide adventurers while testing their wisdom.
Speech style: Formal, uses archaic words like "thee" and "hark".`
});
// Or set after creation
npc.setCharacterDesign(`You are a cheerful tavern keeper named Rose.
You love gossip and know everything happening in town.`);Get Current Design
const design = npc.getCharacterDesign();
console.log('Current character:', design);Memory System
Memories let you dynamically add context that affects NPC responses without changing the base character design.
Set Memories
// Add player information
npc.setMemory('playerName', 'Aragorn');
npc.setMemory('playerClass', 'Ranger');
npc.setMemory('relationship', 'Friendly - helped the NPC before');
// Add world state
npc.setMemory('currentQuest', 'Defeat the Dragon of Mount Doom');
npc.setMemory('worldState', 'The kingdom is at war with the northern tribes');
// NPC responses now incorporate these memories
const response = await npc.talk('Do you remember me?');
// NPC will reference knowing Aragorn, the ranger who helped beforeUpdate Memories
// Memories can be updated as the game progresses
npc.setMemory('relationship', 'Very friendly - saved the NPC\'s life');
npc.setMemory('playerLevel', '15');Remove Memories
// Remove specific memory
npc.setMemory('temporaryBuff', null); // or empty string
// Clear all memories (keeps character design)
npc.clearMemories();Get Memories
// Get specific memory
const playerName = npc.getMemory('playerName');
// Get all memory names
const memoryNames = npc.getMemoryNames();
console.log('Stored memories:', memoryNames);
// ['playerName', 'playerClass', 'relationship', ...]Actions (Tool Calling)
NPCs can trigger game actions based on conversation context.
Define Actions
const actions = [
{
actionName: 'give_item',
description: 'Give an item to the player as a reward or gift',
parameters: [
{
name: 'itemName',
type: 'string',
description: 'Name of the item to give',
required: true
},
{
name: 'quantity',
type: 'number',
description: 'How many to give',
required: false
}
]
},
{
actionName: 'start_quest',
description: 'Offer a new quest to the player',
parameters: [
{
name: 'questId',
type: 'string',
description: 'The quest identifier',
required: true
}
]
},
{
actionName: 'change_attitude',
description: 'Change NPC attitude toward player',
parameters: [
{
name: 'attitude',
type: 'stringEnum',
description: 'New attitude level',
enumOptions: ['hostile', 'neutral', 'friendly', 'ally']
}
]
}
];Talk with Actions
const response = await npc.talkWithActions(
'I completed your quest! The dragon is defeated.',
actions
);
console.log('NPC says:', response.text);
if (response.hasActions) {
for (const action of response.actionCalls) {
console.log('Action:', action.actionName);
console.log('Arguments:', action.arguments);
// Execute in your game
switch (action.actionName) {
case 'give_item':
givePlayerItem(action.arguments.itemName, action.arguments.quantity);
break;
case 'start_quest':
startQuest(action.arguments.questId);
break;
case 'change_attitude':
updateNpcAttitude(action.arguments.attitude);
break;
}
}
}Streaming with Actions
await npc.talkWithActionsStream(
'Can you help me?',
actions,
(chunk) => {
displayText(chunk);
},
(response) => {
if (response.hasActions) {
handleActions(response.actionCalls);
}
}
);Report Action Results
For multi-turn interactions where the NPC needs to know action outcomes:
const response = await npc.talkWithActions('Open the chest', actions);
// Execute actions and report results back
for (const action of response.actionCalls) {
const result = executeAction(action);
npc.reportActionResult(action.id, result);
}
// Or report multiple at once
npc.reportActionResults({
'call_abc123': 'Chest opened successfully. Found 50 gold.',
'call_def456': 'Player received the golden key.'
});Reply Predictions
Automatically generate suggested responses the player might say next.
Enable Auto-Prediction
const npc = sdk.createNPCClient({
characterDesign: 'You are a quest giver.',
generateReplyPrediction: true,
predictionCount: 4 // Generate 4 suggestions
});
// Listen for predictions
npc.on('replyPredictions', (predictions) => {
console.log('Suggested replies:', predictions);
// Display as clickable options in UI
displayReplyOptions(predictions);
});
await npc.talk('Welcome, adventurer! I have a dangerous mission.');
// Predictions generated automatically after responseManual Prediction
// Generate predictions on demand
const predictions = await npc.generateReplyPredictions(4);
console.log(predictions);
// [
// "What kind of mission?",
// "I'm ready for anything!",
// "What's the reward?",
// "Sounds too dangerous for me."
// ]Configure Predictions
// Enable/disable
npc.setGenerateReplyPrediction(true);
// Change count (2-6)
npc.setPredictionCount(3);History Management
View History
// Get full history
const history = npc.getHistory();
console.log(`${history.length} messages in conversation`);
// Get history length
const length = npc.getHistoryLength();Clear History
// Clear conversation (keeps character design and memories)
npc.clearHistory();Revert Messages
// Undo last user/assistant exchange
const reverted = npc.revertHistory();
if (reverted) {
console.log('Last exchange removed');
}
// Remove specific number of messages
const removed = npc.revertChatMessages(4);
console.log(`Removed ${removed} messages`);Append Messages
// Manually add to history
npc.appendMessage({
role: 'user',
content: 'This is injected context'
});
// Shorthand
npc.appendChatMessage('assistant', 'Previous response to remember');Save and Load
Persist conversations across game sessions.
Save Conversation
// Serialize to JSON string
const saveData = npc.saveHistory();
// Store in your game's save system
localStorage.setItem('npc_wizard_conversation', saveData);
// Or: saveToDatabase(playerId, 'wizard_npc', saveData);Load Conversation
// Retrieve saved data
const saveData = localStorage.getItem('npc_wizard_conversation');
if (saveData) {
const success = npc.loadHistory(saveData);
if (success) {
console.log('Conversation restored!');
// NPC remembers previous conversation
}
}Save Data Structure
The saved data includes:
- Character design
- All memories
- Conversation history
interface ConversationSaveData {
characterDesign: string;
memories: Array<{ name: string; content: string }>;
history: Message[];
}Events
NPCClient extends EventEmitter for reactive programming.
// Response events
npc.on('response', (text) => {
console.log('NPC responded:', text);
});
npc.on('actions', (actionCalls) => {
console.log('NPC triggered actions:', actionCalls);
});
npc.on('replyPredictions', (predictions) => {
updateUI(predictions);
});
// Memory events
npc.on('memory_set', (name, content) => {
console.log(`Memory "${name}" set to:`, content);
});
npc.on('memory_removed', (name) => {
console.log(`Memory "${name}" removed`);
});
npc.on('memories_cleared', () => {
console.log('All memories cleared');
});
// History events
npc.on('history_cleared', () => {
console.log('Conversation reset');
});
npc.on('history_reverted', () => {
console.log('History reverted');
});
npc.on('history_loaded', () => {
console.log('Previous conversation loaded');
});Configuration Reference
NPCConfig Options
| Option | Type | Default | Description |
|---|---|---|---|
characterDesign | string | - | NPC personality and behavior prompt |
model | string | - | AI model to use |
temperature | number | 0.7 | Response randomness (0.0-2.0) |
maxHistoryLength | number | 50 | Maximum messages to keep |
generateReplyPrediction | boolean | false | Auto-generate reply suggestions |
predictionCount | number | 4 | Number of predictions (2-6) |
fastModel | string | - | Faster model for predictions |
Action Parameter Types
| Type | Description |
|---|---|
string | Free text input |
number | Numeric value |
boolean | True/false |
stringEnum | One of specified options |
Best Practices
1. Design Rich Characters
// Good: detailed character with clear personality
const npc = sdk.createNPCClient({
characterDesign: `You are Greta, the blacksmith.
Background: Former adventurer, retired after losing her arm to a dragon.
Personality: Gruff but kind-hearted. Respects strength and courage.
Speech: Direct, uses forge metaphors. Calls everyone "spark".
Secret: She knows the location of the legendary Dragonbane sword.`
});
// Bad: vague character
const npc = sdk.createNPCClient({
characterDesign: 'You are a blacksmith.'
});2. Use Memories for Dynamic Context
// Good: update memories as game state changes
npc.setMemory('playerReputation', 'Hero of the village');
npc.setMemory('lastInteraction', 'Player bought a sword yesterday');
npc.setMemory('shopInventory', 'Low on iron, has rare mithril');
// Bad: putting everything in character design3. Limit History for Performance
// Good: reasonable history limit
const npc = sdk.createNPCClient({
characterDesign: '...',
maxHistoryLength: 20 // Enough context, good performance
});
// Bad: unlimited history
const npc = sdk.createNPCClient({
characterDesign: '...',
maxHistoryLength: 1000 // Too much, slow and expensive
});4. Handle Actions Properly
// Good: validate and execute actions safely
if (response.hasActions) {
for (const action of response.actionCalls) {
if (canExecuteAction(action.actionName)) {
const result = executeAction(action);
npc.reportActionResult(action.id, result);
} else {
npc.reportActionResult(action.id, 'Action not available');
}
}
}5. Save Conversations at Key Points
// Good: save at natural break points
async function endConversation() {
const saveData = npc.saveHistory();
await saveToCloud(playerId, npcId, saveData);
}
// Save when player leaves area, game saves, etc.Complete Example
import { PlayKitSDK } from 'playkit-sdk';
const sdk = new PlayKitSDK({
gameId: 'your-game-id',
developerToken: 'your-token'
});
await sdk.initialize();
// Create NPC
const merchant = sdk.createNPCClient({
characterDesign: `You are Marcus, a traveling merchant.
You sell potions, scrolls, and curiosities from distant lands.
You're friendly but always looking for a good deal.
You have rumors about nearby dungeons to share with good customers.`,
temperature: 0.8,
generateReplyPrediction: true,
predictionCount: 3
});
// Set up memories
merchant.setMemory('playerName', 'Hero');
merchant.setMemory('playerGold', '500');
merchant.setMemory('previousPurchases', 'Health potion x2');
// Define shop actions
const shopActions = [
{
actionName: 'sell_item',
description: 'Sell an item to the player',
parameters: [
{ name: 'item', type: 'string', required: true },
{ name: 'price', type: 'number', required: true }
]
},
{
actionName: 'share_rumor',
description: 'Share information about nearby locations',
parameters: [
{ name: 'location', type: 'string', required: true },
{ name: 'details', type: 'string', required: true }
]
}
];
// Listen for events
merchant.on('replyPredictions', (predictions) => {
displayDialogueOptions(predictions);
});
// Have conversation
const greeting = await merchant.talk('Hello there!');
displayNpcText(greeting);
// Player wants to buy something
const response = await merchant.talkWithActions(
'Do you have any healing potions?',
shopActions
);
displayNpcText(response.text);
if (response.hasActions) {
for (const action of response.actionCalls) {
if (action.actionName === 'sell_item') {
showPurchaseDialog(action.arguments.item, action.arguments.price);
}
}
}
// Save for later
const saveData = merchant.saveHistory();
localStorage.setItem('merchant_conversation', saveData);Next Steps
- Learn about Text Generation for lower-level AI control
- Explore Structured Output for generating game data
- Check API Reference for complete method details