RuneHive-Game
Loading...
Searching...
No Matches
com.runehive.content.ai.OpenAIService Class Reference
Collaboration diagram for com.runehive.content.ai.OpenAIService:

Classes

class  ChatMessage

Public Member Functions

void clearConversationHistory (String username)
int getActiveSessionCount ()
int getMinWordCount ()
void initialize ()
boolean isInitialized ()
CompletableFuture< String > processPlayerMessage (String username, String message, int npcId)
void shutdown ()

Static Public Member Functions

static OpenAIService getInstance ()

Private Member Functions

String hashUsername (String username)
void loadApiKey () throws IOException
 OpenAIService ()

Private Attributes

String apiKey
final ConcurrentHashMap< String, List< ChatMessage > > conversationHistory
final ExecutorService executorService
final AtomicBoolean initialized = new AtomicBoolean(false)
OpenAIClient openAIClient

Static Private Attributes

static final String API_KEY_ENV = "OPENAI_API_KEY"
static final String API_KEY_FILE = "openai-java-key.txt"
static final OpenAIService INSTANCE = new OpenAIService()
static final Logger logger = LoggerFactory.getLogger(OpenAIService.class)
static final int MAX_CONVERSATION_HISTORY = 10
static final int MAX_TOKENS = 500
static final double TEMPERATURE = 0.8

Detailed Description

Definition at line 25 of file OpenAIService.java.

Constructor & Destructor Documentation

◆ OpenAIService()

com.runehive.content.ai.OpenAIService.OpenAIService ( )
private

Definition at line 42 of file OpenAIService.java.

42 {
43 this.executorService = Executors.newFixedThreadPool(4);
44 this.conversationHistory = new ConcurrentHashMap<>();
45 }

Referenced by getInstance().

Here is the caller graph for this function:

Member Function Documentation

◆ clearConversationHistory()

void com.runehive.content.ai.OpenAIService.clearConversationHistory ( String username)

Definition at line 175 of file OpenAIService.java.

175 {
176 if (conversationHistory.remove(username) != null) {
177 logger.debug("Cleared conversation history for user: {}", hashUsername(username));
178 }
179 }

References conversationHistory, hashUsername(), and logger.

Here is the call graph for this function:

◆ getActiveSessionCount()

int com.runehive.content.ai.OpenAIService.getActiveSessionCount ( )

Definition at line 195 of file OpenAIService.java.

195 {
196 return conversationHistory.size();
197 }

References conversationHistory.

◆ getInstance()

OpenAIService com.runehive.content.ai.OpenAIService.getInstance ( )
static

Definition at line 47 of file OpenAIService.java.

47 {
48 return INSTANCE;
49 }

References INSTANCE, and OpenAIService().

Referenced by com.runehive.content.ai.LazyAIManager.initializeOpenAIServices().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getMinWordCount()

int com.runehive.content.ai.OpenAIService.getMinWordCount ( )

Definition at line 191 of file OpenAIService.java.

191 {
192 return 0;
193 }

◆ hashUsername()

String com.runehive.content.ai.OpenAIService.hashUsername ( String username)
private

Definition at line 181 of file OpenAIService.java.

181 {
182 if (username == null) return "null";
183 int hash = username.hashCode();
184 return "user_" + Integer.toHexString(hash);
185 }

Referenced by clearConversationHistory(), and processPlayerMessage().

Here is the caller graph for this function:

◆ initialize()

void com.runehive.content.ai.OpenAIService.initialize ( )

Definition at line 51 of file OpenAIService.java.

51 {
52 if (initialized.compareAndSet(false, true)) {
53 logger.info("Initializing Official OpenAI Service with GPT-4o-mini model");
54
55 try {
56 loadApiKey();
57
58 openAIClient = OpenAIOkHttpClient.builder()
59 .apiKey(apiKey)
60 .timeout(Duration.ofSeconds(30))
61 .maxRetries(3)
62 .build();
63
64 logger.info("Official OpenAI Service initialized and ready for GPT-4o-mini requests");
65 } catch (Exception e) {
66 logger.error("Failed to initialize Official OpenAI Service", e);
67 initialized.set(false);
68 throw new RuntimeException("OpenAI Service initialization failed", e);
69 }
70 }
71 }

References apiKey, initialized, loadApiKey(), logger, and openAIClient.

Here is the call graph for this function:

◆ isInitialized()

boolean com.runehive.content.ai.OpenAIService.isInitialized ( )

Definition at line 187 of file OpenAIService.java.

187 {
188 return initialized.get();
189 }

References initialized.

◆ loadApiKey()

void com.runehive.content.ai.OpenAIService.loadApiKey ( ) throws IOException
private

Definition at line 73 of file OpenAIService.java.

73 {
74 apiKey = System.getenv(API_KEY_ENV);
75
76 if (apiKey != null && !apiKey.trim().isEmpty()) {
77 apiKey = apiKey.trim();
78 if (apiKey.matches("^sk-[a-zA-Z0-9\\-_]{20,}$")) {
79 logger.info("OpenAI API key loaded from environment variable");
80 return;
81 } else {
82 logger.warn("Invalid API key format in environment variable - must match pattern: sk-[alphanumeric]{20+}");
83 apiKey = null;
84 }
85 }
86
87 try {
88 Path keyFile = Paths.get(API_KEY_FILE);
89 if (Files.exists(keyFile)) {
90 byte[] bytes = Files.readAllBytes(keyFile);
91 apiKey = new String(bytes, "UTF-8").trim();
92 if (apiKey.isEmpty() || !apiKey.matches("^sk-[a-zA-Z0-9\\-_]{20,}$")) {
93 throw new IllegalArgumentException("Invalid API key format in file - must match pattern: sk-[alphanumeric]{20+}");
94 }
95 logger.warn("OpenAI API key loaded from file - consider using environment variable {} for better security", API_KEY_ENV);
96 return;
97 }
98 } catch (IOException e) {
99 logger.error("Could not read API key file: {}", API_KEY_FILE);
100 }
101
102 throw new IOException("OpenAI API key not found. Set environment variable " + API_KEY_ENV +
103 " or create file " + API_KEY_FILE + " with a valid API key starting with 'sk-'");
104 }

References API_KEY_ENV, API_KEY_FILE, apiKey, and logger.

Referenced by initialize().

Here is the caller graph for this function:

◆ processPlayerMessage()

CompletableFuture< String > com.runehive.content.ai.OpenAIService.processPlayerMessage ( String username,
String message,
int npcId )

Definition at line 106 of file OpenAIService.java.

106 {
107 return CompletableFuture.supplyAsync(() -> {
108 try {
109 if (!initialized.get()) {
110 logger.warn("OpenAI Service not initialized");
111 return "The mystical networks are not yet connected. Please try again.";
112 }
113
114 List<ChatMessage> history = conversationHistory.computeIfAbsent(username,
115 k -> new ArrayList<>());
116
117 history.add(new ChatMessage("user", message));
118
119 if (history.size() > MAX_CONVERSATION_HISTORY) {
120 history = new ArrayList<>(history.subList(
121 history.size() - MAX_CONVERSATION_HISTORY, history.size()));
122 conversationHistory.put(username, history);
123 }
124
125 ChatCompletionCreateParams.Builder paramsBuilder = ChatCompletionCreateParams.builder()
126 .model(ChatModel.GPT_4O_MINI)
127 .maxCompletionTokens(MAX_TOKENS)
128 .temperature(TEMPERATURE)
129 .addDeveloperMessage(
130 "You are a helpful AI assistant. Provide accurate, concise, and direct answers to questions. " +
131 "Keep responses under 150 tokens while ensuring they fully answer the question."
132 );
133
134 for (ChatMessage msg : history) {
135 if ("user".equals(msg.getRole())) {
136 paramsBuilder.addUserMessage(msg.getContent());
137 } else if ("assistant".equals(msg.getRole())) {
138 paramsBuilder.addAssistantMessage(msg.getContent());
139 }
140 }
141
142 ChatCompletionCreateParams params = paramsBuilder.build();
143
144 logger.debug("Making OpenAI API request for user: {}", hashUsername(username));
145 ChatCompletion completion = openAIClient.chat().completions().create(params);
146
147 if (completion.choices().isEmpty()) {
148 logger.warn("No choices returned from OpenAI API");
149 return "The mystical spirits did not respond. Please try again.";
150 }
151
152 String response = completion.choices().get(0).message().content().orElse("");
153 if (response.isEmpty()) {
154 logger.warn("Empty response from OpenAI API");
155 return "The AI spirits whispered too quietly. Please try again.";
156 }
157
158 history.add(new ChatMessage("assistant", response));
159
160 completion.usage().ifPresent(usage -> {
161 logger.info("OpenAI API usage for {}: prompt_tokens={}, completion_tokens={}, total_tokens={}",
162 hashUsername(username), usage.promptTokens(), usage.completionTokens(), usage.totalTokens());
163 });
164
165 logger.debug("Successfully processed OpenAI request for user: {}", hashUsername(username));
166 return response;
167
168 } catch (Exception e) {
169 logger.error("Error processing OpenAI request for user: " + username, e);
170 return "The mystical connection encountered turbulence. Please try again later.";
171 }
172 }, executorService);
173 }

References conversationHistory, executorService, hashUsername(), initialized, logger, MAX_CONVERSATION_HISTORY, MAX_TOKENS, openAIClient, and TEMPERATURE.

Referenced by com.runehive.content.ai.AIDialogueHandler.processGandalfAIMessage().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ shutdown()

void com.runehive.content.ai.OpenAIService.shutdown ( )

Definition at line 199 of file OpenAIService.java.

199 {
200 logger.info("Shutting down Official OpenAI Service...");
201
202 conversationHistory.clear();
203
204 if (executorService != null && !executorService.isShutdown()) {
205 executorService.shutdown();
206 }
207
208 openAIClient = null;
209
210 initialized.set(false);
211 logger.info("Official OpenAI Service shutdown complete");
212 }

References conversationHistory, executorService, initialized, logger, and openAIClient.

Member Data Documentation

◆ API_KEY_ENV

final String com.runehive.content.ai.OpenAIService.API_KEY_ENV = "OPENAI_API_KEY"
staticprivate

Definition at line 28 of file OpenAIService.java.

Referenced by loadApiKey().

◆ API_KEY_FILE

final String com.runehive.content.ai.OpenAIService.API_KEY_FILE = "openai-java-key.txt"
staticprivate

Definition at line 29 of file OpenAIService.java.

Referenced by loadApiKey().

◆ apiKey

String com.runehive.content.ai.OpenAIService.apiKey
private

Definition at line 40 of file OpenAIService.java.

Referenced by initialize(), and loadApiKey().

◆ conversationHistory

final ConcurrentHashMap<String, List<ChatMessage> > com.runehive.content.ai.OpenAIService.conversationHistory
private

◆ executorService

final ExecutorService com.runehive.content.ai.OpenAIService.executorService
private

Definition at line 36 of file OpenAIService.java.

Referenced by processPlayerMessage(), and shutdown().

◆ initialized

final AtomicBoolean com.runehive.content.ai.OpenAIService.initialized = new AtomicBoolean(false)
private

Definition at line 38 of file OpenAIService.java.

Referenced by initialize(), isInitialized(), processPlayerMessage(), and shutdown().

◆ INSTANCE

final OpenAIService com.runehive.content.ai.OpenAIService.INSTANCE = new OpenAIService()
staticprivate

Definition at line 34 of file OpenAIService.java.

Referenced by getInstance().

◆ logger

final Logger com.runehive.content.ai.OpenAIService.logger = LoggerFactory.getLogger(OpenAIService.class)
staticprivate

◆ MAX_CONVERSATION_HISTORY

final int com.runehive.content.ai.OpenAIService.MAX_CONVERSATION_HISTORY = 10
staticprivate

Definition at line 32 of file OpenAIService.java.

Referenced by processPlayerMessage().

◆ MAX_TOKENS

final int com.runehive.content.ai.OpenAIService.MAX_TOKENS = 500
staticprivate

Definition at line 30 of file OpenAIService.java.

Referenced by processPlayerMessage().

◆ openAIClient

OpenAIClient com.runehive.content.ai.OpenAIService.openAIClient
private

Definition at line 39 of file OpenAIService.java.

Referenced by initialize(), processPlayerMessage(), and shutdown().

◆ TEMPERATURE

final double com.runehive.content.ai.OpenAIService.TEMPERATURE = 0.8
staticprivate

Definition at line 31 of file OpenAIService.java.

Referenced by processPlayerMessage().


The documentation for this class was generated from the following file: