PlayKit.ai
Text Generation

Text Generation

Implement AI conversation and text generation using PlayKit_AIChatClient

Before You Begin

Create a Chat Client

Use the factory method to create a chat client:

// Use default model
var chatClient = PlayKitSDK.Factory.CreateChatClient();

// Specify model
var chatClient = PlayKitSDK.Factory.CreateChatClient("gpt-4");

Basic Text Generation

Use the TextGenerationAsync() method for basic text generation. This is the simplest approach, suitable for single conversations or scenarios requiring complete responses.

Simple Conversation

using Cysharp.Threading.Tasks;
using PlayKit_SDK;
using PlayKit_SDK.Public;
using System.Collections.Generic;
using UnityEngine;

public class SimpleChat : MonoBehaviour
{
    async void Start()
    {
        var chatClient = PlayKitSDK.Factory.CreateChatClient();

        var messages = new List<PlayKit_ChatMessage>
        {
            new PlayKit_ChatMessage { Role = "user", Content = "Please introduce Unity Engine in one sentence." }
        };

        var config = new PlayKit_ChatConfig(messages) { Temperature = 0.7f };
        var result = await chatClient.TextGenerationAsync(config, this.GetCancellationTokenOnDestroy());

        if (result.Success)
            Debug.Log($"AI response: {result.Response}");
        else
            Debug.LogError($"Request failed: {result.ErrorMessage}");
    }
}

Multi-turn Conversation

Implement multi-turn conversations by maintaining conversation history:

using Cysharp.Threading.Tasks;
using PlayKit_SDK;
using PlayKit_SDK.Public;
using System.Collections.Generic;
using UnityEngine;

public class MultiTurnChat : MonoBehaviour
{
    private PlayKit_AIChatClient chatClient;
    private List<PlayKit_ChatMessage> conversationHistory = new List<PlayKit_ChatMessage>();

    void Start()
    {
        chatClient = PlayKitSDK.Factory.CreateChatClient();

        conversationHistory.Add(new PlayKit_ChatMessage
        {
            Role = "system",
            Content = "You are an intelligent assistant in a game that answers player questions."
        });
    }

    public async UniTask<string> SendMessage(string userMessage)
    {
        conversationHistory.Add(new PlayKit_ChatMessage { Role = "user", Content = userMessage });

        var config = new PlayKit_ChatConfig(conversationHistory) { Temperature = 0.7f };
        var result = await chatClient.TextGenerationAsync(config, this.GetCancellationTokenOnDestroy());

        if (result.Success)
        {
            conversationHistory.Add(new PlayKit_ChatMessage { Role = "assistant", Content = result.Response });

            return result.Response;
        }
        else
        {
            Debug.LogError($"Request failed: {result.ErrorMessage}");
            return null;
        }
    }

    // Clear conversation history (keep system message)
    public void ResetConversation()
    {
        var systemMessage = conversationHistory[0];
        conversationHistory.Clear();
        conversationHistory.Add(systemMessage);
    }
}

Usage Example

var chat = GetComponent<MultiTurnChat>();

var reply1 = await chat.SendMessage("How do I get gold coins in the game?");
Debug.Log(reply1);

var reply2 = await chat.SendMessage("Are there other ways besides that?");
Debug.Log(reply2);

Streaming Responses

Use the TextChatStreamAsync() method to receive AI-generated text in real-time. This approach is suitable for typewriter effects and provides a better user experience.

Basic Streaming Chat

using Cysharp.Threading.Tasks;
using PlayKit_SDK;
using PlayKit_SDK.Public;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class StreamingChat : MonoBehaviour
{
    [SerializeField] private Text responseText;

    private PlayKit_AIChatClient chatClient;
    private string currentResponse = "";

    void Start()
    {
        chatClient = PlayKitSDK.Factory.CreateChatClient();
    }

    public async UniTask SendStreamingMessage(string userMessage)
    {
        currentResponse = "";
        responseText.text = "";

        var messages = new List<PlayKit_ChatMessage>
        {
            new PlayKit_ChatMessage { Role = "user", Content = userMessage }
        };

        var config = new PlayKit_ChatStreamConfig
        {
            Messages = messages,
            Temperature = 0.7f
        };

        await chatClient.TextChatStreamAsync(
            config,
            onNewChunk: (chunk) =>
            {
                // Called when new text chunk is received
                currentResponse += chunk;
                responseText.text = currentResponse;
            },
            onConcluded: (fullResponse) =>
            {
                // Called when full response is complete
                Debug.Log($"Complete response: {fullResponse}");
                currentResponse = fullResponse;
            },
            this.GetCancellationTokenOnDestroy()
        );
    }
}

Typewriter Effect with Streaming

public class TypewriterStreamingChat : MonoBehaviour
{
    [SerializeField] private Text responseText;
    [SerializeField] private float charactersPerSecond = 30f;

    private PlayKit_AIChatClient chatClient;
    private Queue<char> characterQueue = new Queue<char>();
    private bool isTyping = false;

    void Start()
    {
        chatClient = PlayKitSDK.Factory.CreateChatClient();
    }

    public async UniTask SendStreamingMessage(string userMessage)
    {
        characterQueue.Clear();
        responseText.text = "";

        var messages = new List<PlayKit_ChatMessage>
        {
            new PlayKit_ChatMessage { Role = "user", Content = userMessage }
        };

        var config = new PlayKit_ChatStreamConfig
        {
            Messages = messages,
            Temperature = 0.7f
        };

        // Start typewriter effect
        if (!isTyping)
        {
            StartTypewriter();
        }

        await chatClient.TextChatStreamAsync(
            config,
            onNewChunk: (chunk) =>
            {
                // Add new characters to queue
                foreach (char c in chunk)
                {
                    characterQueue.Enqueue(c);
                }
            },
            onConcluded: (fullResponse) =>
            {
                Debug.Log("Streaming response complete");
            },
            this.GetCancellationTokenOnDestroy()
        );
    }

    private async void StartTypewriter()
    {
        isTyping = true;
        string displayedText = "";

        while (isTyping)
        {
            if (characterQueue.Count > 0)
            {
                char nextChar = characterQueue.Dequeue();
                displayedText += nextChar;
                responseText.text = displayedText;

                await UniTask.Delay((int)(1000f / charactersPerSecond));
            }
            else
            {
                await UniTask.Delay(100); // Wait for new characters
            }
        }
    }

    void OnDestroy()
    {
        isTyping = false;
    }
}

System Prompts

System messages (system role) define AI behavior, character, and knowledge background.

Setting a Character

var messages = new List<PlayKit_ChatMessage>
{
    new PlayKit_ChatMessage
    {
        Role = "system",
        Content = @"You are a master blacksmith named Grin in a medieval fantasy game.
You are an expert in forging weapons and armor, running the best smithy in the village.
You have an outgoing personality and love sharing forging knowledge with adventurers.
Always respond as the blacksmith character."
    },
    new PlayKit_ChatMessage
    {
        Role = "user",
        Content = "Hello, I want to forge a sword."
    }
};

Providing Context

new PlayKit_ChatMessage
{
    Role = "system",
    Content = @"You are a game quest assistant.

Current game state:
- Player Level: 15
- Current Location: Elven Forest
- Main Story Progress: Chapter 2 complete
- Available Gold: 500

Answer the player's questions based on this information and provide relevant advice."
}

Configuration Options

PlayKit_ChatConfig and PlayKit_ChatStreamConfig provide the following options:

Temperature

Controls response randomness and creativity:

var config = new PlayKit_ChatConfig(messages) { Temperature = 0.7f };

Recommended values:

  • 0.0-0.3: Deterministic responses, suitable for factual Q&A, game rules
  • 0.7: Balances creativity and consistency, suitable for most conversations
  • 1.2-2.0: Highly creative, suitable for story generation, roleplay

Managing Conversation History

Limiting History Length

Long conversation history increases API costs and response time. Consider limiting history:

public class ChatHistoryManager
{
    private List<PlayKit_ChatMessage> history = new List<PlayKit_ChatMessage>();
    private const int MAX_HISTORY_LENGTH = 20;

    public void AddMessage(string role, string content)
    {
        history.Add(new PlayKit_ChatMessage { Role = role, Content = content });

        // Keep system message and recent conversations
        if (history.Count > MAX_HISTORY_LENGTH)
        {
            // Keep the first system message
            var systemMessage = history.Find(m => m.Role == "system");
            var recentMessages = history.GetRange(
                history.Count - MAX_HISTORY_LENGTH + 1,
                MAX_HISTORY_LENGTH - 1
            );

            history.Clear();
            if (systemMessage != null)
            {
                history.Add(systemMessage);
            }
            history.AddRange(recentMessages);
        }
    }

    public List<PlayKit_ChatMessage> GetHistory()
    {
        return history;
    }
}

Summarizing History

For long conversations, periodically summarize the history:

public async UniTask<List<PlayKit_ChatMessage>> SummarizeHistory(
    List<PlayKit_ChatMessage> history)
{
    var chatClient = PlayKitSDK.Factory.CreateChatClient();

    var summarizeMessages = new List<PlayKit_ChatMessage>
    {
        new PlayKit_ChatMessage { Role = "system", Content = "Please summarize the key information from the following conversation concisely." },
        new PlayKit_ChatMessage { Role = "user", Content = ConvertHistoryToText(history) }
    };

    var config = new PlayKit_ChatConfig(summarizeMessages) { Temperature = 0.3f };
    var result = await chatClient.TextGenerationAsync(config, this.GetCancellationTokenOnDestroy());

    if (result.Success)
    {
        return new List<PlayKit_ChatMessage>
        {
            history[0],
            new PlayKit_ChatMessage { Role = "system", Content = $"Summary of previous conversation: {result.Response}" }
        };
    }

    return history;
}

private string ConvertHistoryToText(List<PlayKit_ChatMessage> history)
{
    var text = "";
    foreach (var msg in history)
    {
        if (msg.Role != "system")
        {
            text += $"{msg.Role}: {msg.Content}\n";
        }
    }
    return text;
}

Error Handling

Retry Mechanism

public async UniTask<PlayKit_AIResult<string>> SendMessageWithRetry(
    string message, int maxRetries = 3)
{
    var chatClient = PlayKitSDK.Factory.CreateChatClient();
    var messages = new List<PlayKit_ChatMessage>
    {
        new PlayKit_ChatMessage { Role = "user", Content = message }
    };

    var config = new PlayKit_ChatConfig(messages) { Temperature = 0.7f };

    for (int i = 0; i < maxRetries; i++)
    {
        var result = await chatClient.TextGenerationAsync(
            config,
            this.GetCancellationTokenOnDestroy()
        );

        if (result.Success)
        {
            return result;
        }

        Debug.LogWarning($"Request failed, retry {i + 1}/{maxRetries}: {result.ErrorMessage}");

        if (i < maxRetries - 1)
        {
            await UniTask.Delay(1000 * (i + 1)); // Incremental delay
        }
    }

    return new PlayKit_AIResult<string>
    {
        Success = false,
        ErrorMessage = "Max retries reached"
    };
}

Timeout Handling

public async UniTask<string> SendMessageWithTimeout(string message, int timeoutSeconds = 30)
{
    var cts = new CancellationTokenSource();
    cts.CancelAfterSlim(TimeSpan.FromSeconds(timeoutSeconds));

    try
    {
        var chatClient = PlayKitSDK.Factory.CreateChatClient();
        var messages = new List<PlayKit_ChatMessage>
        {
            new PlayKit_ChatMessage { Role = "user", Content = message }
        };

        var config = new PlayKit_ChatConfig(messages) { Temperature = 0.7f };
        var result = await chatClient.TextGenerationAsync(config, cts.Token);

        if (result.Success)
        {
            return result.Response;
        }
        else
        {
            Debug.LogError($"Request failed: {result.ErrorMessage}");
            return null;
        }
    }
    catch (OperationCanceledException)
    {
        Debug.LogError("Request timed out");
        return null;
    }
    finally
    {
        cts?.Dispose();
    }
}

Debug Tools

The SDK provides helper methods for printing conversation history:

var messages = new List<PlayKit_ChatMessage> { /* ... */ };

// Print conversation in formatted style in Console
PlayKit_AIChatClient.PrintPrettyChatMessages(messages, "Current Conversation History");

Output example:

==================== Current Conversation History ====================
[system] You are a friendly game assistant.
[user] Hello
[assistant] Hello! How can I help you?
===================================================

Best Practices

  1. Use system messages: Always set a system message to define AI behavior
  2. Limit history length: Avoid long conversation history to reduce costs
  3. Set temperature appropriately: Choose suitable temperature values for your scenario
  4. Use streaming responses: Use streaming in chat interfaces for better UX
  5. Handle errors: Always check Success and handle error cases
  6. Use CancellationToken: Avoid continued execution after object destruction
  7. Consider NPC Client: For game NPCs, use PlayKit_NPC for simplicity

Next Steps