Commit 5d7dceae by Jonathan Thomas

Created new prompt templates which are loaded from text files, and support for…

Created new prompt templates which are loaded from text files, and support for placeholder / replacements. Also, added in player and entity context as an experiment.
parent bfa46037
Pipeline #11657 passed with stage
in 25 seconds
package com.owlmaddie; package com.owlmaddie;
import net.minecraft.entity.Entity;
import net.minecraft.item.ItemStack;
import net.minecraft.server.network.ServerPlayerEntity;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.ArrayList; import java.util.Map;
public class ChatDataManager { public class ChatDataManager {
...@@ -48,12 +53,45 @@ public class ChatDataManager { ...@@ -48,12 +53,45 @@ public class ChatDataManager {
} }
// Generate greeting // Generate greeting
public void generateMessage(String user_message) { public void generateMessage(ServerPlayerEntity player, String user_message) {
this.status = ChatStatus.PENDING; this.status = ChatStatus.PENDING;
// Add USER Message // Add USER Message
//this.addMessage(user_message, ChatSender.USER); //this.addMessage(user_message, ChatSender.USER);
ChatGPTRequest.fetchMessageFromChatGPT(user_message).thenAccept(output_message -> { // Add PLAYER context information
Map<String, String> contextData = new HashMap<>();
contextData.put("message", user_message);
contextData.put("player_name", player.getDisplayName().getString());
contextData.put("player_health", String.valueOf(player.getHealth()));
contextData.put("player_hunger", String.valueOf(player.getHungerManager().getFoodLevel()));
contextData.put("player_held_item", String.valueOf(player.getMainHandStack().getItem().toString()));
contextData.put("player_biome", player.getWorld().getBiome(player.getBlockPos()).getKey().get().getValue().getPath());
ItemStack feetArmor = player.getInventory().armor.get(0);
ItemStack legsArmor = player.getInventory().armor.get(1);
ItemStack chestArmor = player.getInventory().armor.get(2);
ItemStack headArmor = player.getInventory().armor.get(3);
contextData.put("player_armor_head", headArmor.getItem().toString());
contextData.put("player_armor_chest", chestArmor.getItem().toString());
contextData.put("player_armor_legs", legsArmor.getItem().toString());
contextData.put("player_armor_feet", feetArmor.getItem().toString());
// Get World time (as 24 hour value)
int hours = (int) ((player.getWorld().getTimeOfDay() / 1000 + 6) % 24); // Minecraft day starts at 6 AM
int minutes = (int) (((player.getWorld().getTimeOfDay() % 1000) / 1000.0) * 60);
contextData.put("world_time", String.format("%02d:%02d", hours, minutes));
// Get Entity details
Entity entity = player.getServerWorld().getEntityById(entityId);
if (entity.getCustomName() == null) {
contextData.put("entity_name", "Un-named");
} else {
contextData.put("entity_name", entity.getCustomName().toString());
}
contextData.put("entity_type", entity.getType().getName().getString().toString());
// fetch HTTP response from ChatGPT
ChatGPTRequest.fetchMessageFromChatGPT("chat", contextData).thenAccept(output_message -> {
if (output_message != null) { if (output_message != null) {
// Add ASSISTANT message // Add ASSISTANT message
this.addMessage(output_message, ChatSender.ASSISTANT); this.addMessage(output_message, ChatSender.ASSISTANT);
......
...@@ -2,23 +2,90 @@ package com.owlmaddie; ...@@ -2,23 +2,90 @@ package com.owlmaddie;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.owlmaddie.json.ChatGPTResponse; import com.owlmaddie.json.ChatGPTResponse;
import net.minecraft.resource.ResourceManager;
import net.minecraft.util.Identifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import org.slf4j.Logger; import java.util.ArrayList;
import org.slf4j.LoggerFactory; import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.regex.Pattern;
public class ChatGPTRequest { public class ChatGPTRequest {
public static final Logger LOGGER = LoggerFactory.getLogger("mobgpt"); public static final Logger LOGGER = LoggerFactory.getLogger("mobgpt");
public static CompletableFuture<String> fetchMessageFromChatGPT(String user_message) { static class ChatGPTRequestMessage {
String role;
String content;
public ChatGPTRequestMessage(String role, String content) {
this.role = role;
this.content = content;
}
}
static class ChatGPTRequestPayload {
String model;
List<ChatGPTRequestMessage> messages;
public ChatGPTRequestPayload(String model, List<ChatGPTRequestMessage> messages) {
this.model = model;
this.messages = messages;
}
}
// This method should be called in an appropriate context where ResourceManager is available
public static String loadPromptFromResource(ResourceManager resourceManager, String filePath) {
Identifier fileIdentifier = new Identifier("mobgpt", filePath);
try (InputStream inputStream = resourceManager.getResource(fileIdentifier).get().getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
StringBuilder contentBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
contentBuilder.append(line).append("\n");
}
return contentBuilder.toString();
} catch (Exception e) {
LOGGER.error("Failed to read prompt file", e);
}
return null;
}
// Function to replace placeholders in the template
public static String replacePlaceholders(String template, Map<String, String> replacements) {
String result = template;
for (Map.Entry<String, String> entry : replacements.entrySet()) {
result = result.replaceAll(Pattern.quote("{{" + entry.getKey() + "}}"), entry.getValue());
}
return result.replace("\"", "") ;
}
public static CompletableFuture<String> fetchMessageFromChatGPT(String promptFileName, Map<String, String> context) {
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
try { try {
// Load and prepare the prompt template
String template = "";
if (!promptFileName.isEmpty()) {
// Load prompt from resources
template = loadPromptFromResource(ModInit.serverInstance.getResourceManager(), "prompts/" + promptFileName);
} else if (context.containsKey("user_message")) {
// Use 'user_message' as prompt if no template specified
template = context.get("user_message");
}
// Replace placeholders (if any)
String prompt = replacePlaceholders(template, context);
URL url = new URL("https://api.openai.com/v1/chat/completions"); URL url = new URL("https://api.openai.com/v1/chat/completions");
HttpURLConnection connection = (HttpURLConnection) url.openConnection(); HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST"); connection.setRequestMethod("POST");
...@@ -26,14 +93,15 @@ public class ChatGPTRequest { ...@@ -26,14 +93,15 @@ public class ChatGPTRequest {
connection.setRequestProperty("Authorization", "Bearer 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"); connection.setRequestProperty("Authorization", "Bearer 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");
connection.setDoOutput(true); connection.setDoOutput(true);
String jsonInputString = "{" // Build JSON payload for ChatGPT
+ "\"model\": \"gpt-3.5-turbo\"," List<ChatGPTRequestMessage> messages = new ArrayList<>();
+ "\"messages\": [" messages.add(new ChatGPTRequestMessage("system", "You are a silly Minecraft entity who speaks to the player in short riddles."));
+ "{ \"role\": \"system\", \"content\": \"You are a silly Minecraft entity who speaks to the player in short riddles.\" }," messages.add(new ChatGPTRequestMessage("user", prompt));
+ "{ \"role\": \"user\", \"content\": \"" + user_message.replace("\"", "") + "\" }"
+ "]" // Convert JSON to String
+ "}"; ChatGPTRequestPayload payload = new ChatGPTRequestPayload("gpt-3.5-turbo", messages);
LOGGER.info(jsonInputString); Gson gsonInput = new Gson();
String jsonInputString = gsonInput.toJson(payload);
try (OutputStream os = connection.getOutputStream()) { try (OutputStream os = connection.getOutputStream()) {
byte[] input = jsonInputString.getBytes("utf-8"); byte[] input = jsonInputString.getBytes("utf-8");
...@@ -47,8 +115,8 @@ public class ChatGPTRequest { ...@@ -47,8 +115,8 @@ public class ChatGPTRequest {
response.append(responseLine.trim()); response.append(responseLine.trim());
} }
Gson gson = new Gson(); Gson gsonOutput = new Gson();
ChatGPTResponse chatGPTResponse = gson.fromJson(response.toString(), ChatGPTResponse.class); ChatGPTResponse chatGPTResponse = gsonOutput.fromJson(response.toString(), ChatGPTResponse.class);
if (chatGPTResponse != null && chatGPTResponse.choices != null && !chatGPTResponse.choices.isEmpty()) { if (chatGPTResponse != null && chatGPTResponse.choices != null && !chatGPTResponse.choices.isEmpty()) {
String content = chatGPTResponse.choices.get(0).message.content.replace("\n", " "); String content = chatGPTResponse.choices.get(0).message.content.replace("\n", " ");
LOGGER.info(content); LOGGER.info(content);
......
...@@ -19,7 +19,7 @@ import org.slf4j.LoggerFactory; ...@@ -19,7 +19,7 @@ import org.slf4j.LoggerFactory;
public class ModInit implements ModInitializer { public class ModInit implements ModInitializer {
public static final Logger LOGGER = LoggerFactory.getLogger("mobgpt"); public static final Logger LOGGER = LoggerFactory.getLogger("mobgpt");
private static MinecraftServer serverInstance; public static MinecraftServer serverInstance;
public static final Identifier PACKET_C2S_GREETING = new Identifier("mobgpt", "packet_c2s_greeting"); public static final Identifier PACKET_C2S_GREETING = new Identifier("mobgpt", "packet_c2s_greeting");
public static final Identifier PACKET_C2S_READ_NEXT = new Identifier("mobgpt", "packet_c2s_read_next"); public static final Identifier PACKET_C2S_READ_NEXT = new Identifier("mobgpt", "packet_c2s_read_next");
public static final Identifier PACKET_C2S_START_CHAT = new Identifier("mobgpt", "packet_c2s_start_chat"); public static final Identifier PACKET_C2S_START_CHAT = new Identifier("mobgpt", "packet_c2s_start_chat");
...@@ -48,7 +48,7 @@ public class ModInit implements ModInitializer { ...@@ -48,7 +48,7 @@ public class ModInit implements ModInitializer {
chatData.status == ChatDataManager.ChatStatus.END) { chatData.status == ChatDataManager.ChatStatus.END) {
// Only generate a new greeting if not already doing so // Only generate a new greeting if not already doing so
LOGGER.info("Generate greeting for: " + entity.getType().toString()); LOGGER.info("Generate greeting for: " + entity.getType().toString());
chatData.generateMessage("Hello!"); chatData.generateMessage(player, "Hello!");
} }
} }
}); });
...@@ -106,7 +106,7 @@ public class ModInit implements ModInitializer { ...@@ -106,7 +106,7 @@ public class ModInit implements ModInitializer {
if (chatData.status == ChatDataManager.ChatStatus.END) { if (chatData.status == ChatDataManager.ChatStatus.END) {
// Add new message // Add new message
LOGGER.info("Add new message (" + message + ") to Entity: " + entity.getType().toString()); LOGGER.info("Add new message (" + message + ") to Entity: " + entity.getType().toString());
chatData.generateMessage(message); chatData.generateMessage(player, message);
} }
} }
}); });
......
Please respond directly to the following player's message, as if it was written by this Minecraft entity.
Please do NOT break the 4th wall and refer to the player or game.
Entity Details:
- Name: {{entity_name}}
- Type: {{entity_type}}
Player Details:
- Name: {{player_name}}
- Health: {{player_health}}
- Hunger: {{player_hunger}}
- Biome: {{player_biome}}
- Held Item: {{player_held_item}}
- Armor: Head: {{player_armor_head}}, Chest: {{player_armor_chest}}, Legs: {{player_armor_legs}}, Feet: {{player_armor_feet}}
- Current World Time: {{world_time}} (24 hour format)
Player Message:
{{message}}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment