forked from Upstream/Ollama-Chat
fix: correct OpenAI API request format
This commit addresses a bug where the OpenAI API request was improperly formatted, leading to failed requests. The fix ensures the request payload adheres to the expected structure. Additionally, it incorporates the completed work from the feature-chat-history-save-call branch, which implements chat history persistence and retrieval features. All changes have been tested to confirm successful API communication and history management.
This commit is contained in:
parent
d1b51f6947
commit
2b992e39a7
@ -3,7 +3,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = 'com'
|
||||
version = '1.0.1'
|
||||
version = '1.0.2'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
@ -1 +1 @@
|
||||
rootProject.name = 'ollamachat'
|
||||
rootProject.name = 'OllamaChat'
|
||||
|
@ -5,6 +5,8 @@ import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@ -21,9 +23,16 @@ public class AIService {
|
||||
public CompletableFuture<String> sendRequest(String apiUrl, String apiKey, String model, String prompt) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
// 按照OpenAI Chat API要求构造messages数组
|
||||
List<Map<String, String>> messages = new ArrayList<>();
|
||||
messages.add(Map.of(
|
||||
"role", "user",
|
||||
"content", prompt
|
||||
));
|
||||
|
||||
Map<String, Object> requestBody = Map.of(
|
||||
"model", model,
|
||||
"prompt", prompt,
|
||||
"messages", messages, // 关键修改:将prompt改为messages
|
||||
"stream", false
|
||||
);
|
||||
|
||||
@ -32,12 +41,9 @@ public class AIService {
|
||||
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
|
||||
.uri(URI.create(apiUrl))
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Authorization", "Bearer " + apiKey) // 确保认证头存在
|
||||
.POST(HttpRequest.BodyPublishers.ofString(jsonRequest));
|
||||
|
||||
if (apiKey != null && !apiKey.isEmpty()) {
|
||||
requestBuilder.header("Authorization", "Bearer " + apiKey);
|
||||
}
|
||||
|
||||
HttpRequest request = requestBuilder.build();
|
||||
|
||||
HttpResponse<String> response = httpClient.send(
|
||||
@ -56,3 +62,5 @@ public class AIService {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
27
src/main/java/com/ollamachat/ChatHistoryManager.java
Normal file
27
src/main/java/com/ollamachat/ChatHistoryManager.java
Normal file
@ -0,0 +1,27 @@
|
||||
package com.ollamachat;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class ChatHistoryManager {
|
||||
private final DatabaseManager databaseManager;
|
||||
private final int maxHistory;
|
||||
|
||||
public ChatHistoryManager(DatabaseManager databaseManager, int maxHistory) {
|
||||
this.databaseManager = databaseManager;
|
||||
this.maxHistory = maxHistory;
|
||||
}
|
||||
|
||||
public void savePlayerInfo(Player player) {
|
||||
databaseManager.savePlayerInfo(player.getUniqueId(), player.getName());
|
||||
}
|
||||
|
||||
public void saveChatHistory(UUID playerUuid, String aiModel, String prompt, String response) {
|
||||
databaseManager.saveChatHistory(playerUuid, aiModel, prompt, response);
|
||||
}
|
||||
|
||||
public String getChatHistory(UUID playerUuid, String aiModel) {
|
||||
return databaseManager.getChatHistory(playerUuid, aiModel, maxHistory);
|
||||
}
|
||||
}
|
93
src/main/java/com/ollamachat/DatabaseManager.java
Normal file
93
src/main/java/com/ollamachat/DatabaseManager.java
Normal file
@ -0,0 +1,93 @@
|
||||
package com.ollamachat;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.UUID;
|
||||
|
||||
public class DatabaseManager {
|
||||
private Connection connection;
|
||||
|
||||
public DatabaseManager() {
|
||||
initializeDatabase();
|
||||
}
|
||||
|
||||
private void initializeDatabase() {
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC");
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:plugins/OllamaChat/chat_history.db");
|
||||
createTables();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void createTables() throws SQLException {
|
||||
try (Statement stmt = connection.createStatement()) {
|
||||
stmt.execute("CREATE TABLE IF NOT EXISTS players (" +
|
||||
"uuid TEXT PRIMARY KEY," +
|
||||
"username TEXT NOT NULL)");
|
||||
|
||||
stmt.execute("CREATE TABLE IF NOT EXISTS chat_history (" +
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT," +
|
||||
"player_uuid TEXT NOT NULL," +
|
||||
"ai_model TEXT NOT NULL," +
|
||||
"timestamp DATETIME DEFAULT CURRENT_TIMESTAMP," +
|
||||
"prompt TEXT NOT NULL," +
|
||||
"response TEXT NOT NULL," +
|
||||
"FOREIGN KEY (player_uuid) REFERENCES players(uuid))");
|
||||
}
|
||||
}
|
||||
|
||||
public void savePlayerInfo(UUID uuid, String username) {
|
||||
String sql = "INSERT OR REPLACE INTO players(uuid, username) VALUES(?, ?)";
|
||||
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
|
||||
pstmt.setString(1, uuid.toString());
|
||||
pstmt.setString(2, username);
|
||||
pstmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void saveChatHistory(UUID playerUuid, String aiModel, String prompt, String response) {
|
||||
String sql = "INSERT INTO chat_history(player_uuid, ai_model, prompt, response) VALUES(?, ?, ?, ?)";
|
||||
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
|
||||
pstmt.setString(1, playerUuid.toString());
|
||||
pstmt.setString(2, aiModel);
|
||||
pstmt.setString(3, prompt);
|
||||
pstmt.setString(4, response);
|
||||
pstmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public String getChatHistory(UUID playerUuid, String aiModel, int maxHistory) {
|
||||
StringBuilder history = new StringBuilder();
|
||||
String sql = "SELECT prompt, response FROM chat_history " +
|
||||
"WHERE player_uuid = ? AND ai_model = ? " +
|
||||
"ORDER BY timestamp DESC LIMIT ?";
|
||||
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
|
||||
pstmt.setString(1, playerUuid.toString());
|
||||
pstmt.setString(2, aiModel);
|
||||
pstmt.setInt(3, maxHistory);
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
history.insert(0, "User: " + rs.getString("prompt") + "\n");
|
||||
history.insert(0, "AI: " + rs.getString("response") + "\n");
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return history.toString();
|
||||
}
|
||||
|
||||
public void close() {
|
||||
try {
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package com.ollamachat;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@ -10,12 +11,13 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class Ollamachat extends JavaPlugin implements Listener {
|
||||
@ -31,6 +33,9 @@ public class Ollamachat extends JavaPlugin implements Listener {
|
||||
private Map<String, Boolean> otherAIEnabled;
|
||||
|
||||
private FileConfiguration langConfig;
|
||||
private DatabaseManager databaseManager;
|
||||
private ChatHistoryManager chatHistoryManager;
|
||||
private int maxHistory;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
@ -39,6 +44,12 @@ public class Ollamachat extends JavaPlugin implements Listener {
|
||||
String language = getConfig().getString("language", "en");
|
||||
loadLanguageFile(language);
|
||||
|
||||
updateCommandUsages();
|
||||
|
||||
databaseManager = new DatabaseManager();
|
||||
maxHistory = getConfig().getInt("max-history", 5);
|
||||
chatHistoryManager = new ChatHistoryManager(databaseManager, maxHistory);
|
||||
|
||||
aiService = new AIService();
|
||||
gson = new Gson();
|
||||
|
||||
@ -47,6 +58,19 @@ public class Ollamachat extends JavaPlugin implements Listener {
|
||||
getCommand("aichat").setExecutor(this);
|
||||
}
|
||||
|
||||
private void updateCommandUsages() {
|
||||
String usageOllamachat = getMessage("usage-ollamachat", null);
|
||||
String usageAichat = getMessage("usage-aichat", null);
|
||||
|
||||
getCommand("ollamachat").setUsage(usageOllamachat);
|
||||
getCommand("aichat").setUsage(usageAichat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
databaseManager.close();
|
||||
}
|
||||
|
||||
private void updateConfig() {
|
||||
FileConfiguration config = getConfig();
|
||||
|
||||
@ -59,11 +83,32 @@ public class Ollamachat extends JavaPlugin implements Listener {
|
||||
if (!config.contains("other-ai-configs")) {
|
||||
config.createSection("other-ai-configs");
|
||||
}
|
||||
if (!config.contains("max-history")) {
|
||||
config.set("max-history", 5);
|
||||
}
|
||||
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
private void reloadConfigValues() {
|
||||
File configFile = new File(getDataFolder(), "config.yml");
|
||||
if (!configFile.exists()) {
|
||||
saveDefaultConfig();
|
||||
} else {
|
||||
try {
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
||||
if (!config.contains("ollama-api-url") || !config.contains("model")) {
|
||||
getLogger().warning(getMessage("config-invalid", null));
|
||||
configFile.delete();
|
||||
saveDefaultConfig();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
getLogger().severe(getMessage("config-load-failed", Map.of("error", e.getMessage())));
|
||||
configFile.delete();
|
||||
saveDefaultConfig();
|
||||
}
|
||||
}
|
||||
|
||||
reloadConfig();
|
||||
updateConfig();
|
||||
|
||||
@ -73,6 +118,7 @@ public class Ollamachat extends JavaPlugin implements Listener {
|
||||
triggerPrefix = config.getString("trigger-prefix", "@bot ");
|
||||
maxResponseLength = config.getInt("max-response-length", 500);
|
||||
ollamaEnabled = config.getBoolean("ollama-enabled", true);
|
||||
maxHistory = config.getInt("max-history", 5);
|
||||
|
||||
otherAIConfigs = new HashMap<>();
|
||||
otherAIEnabled = new HashMap<>();
|
||||
@ -117,6 +163,11 @@ public class Ollamachat extends JavaPlugin implements Listener {
|
||||
return message;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
chatHistoryManager.savePlayerInfo(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerChat(AsyncPlayerChatEvent event) {
|
||||
if (!ollamaEnabled) return;
|
||||
@ -137,8 +188,15 @@ public class Ollamachat extends JavaPlugin implements Listener {
|
||||
private void processOllamaQueryAsync(Player player, String prompt) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
String responseBody = aiService.sendRequest(ollamaApiUrl, null, ollamaModel, prompt).join();
|
||||
String history = chatHistoryManager.getChatHistory(player.getUniqueId(), "ollama");
|
||||
|
||||
String context = history + "User: " + prompt;
|
||||
|
||||
String responseBody = aiService.sendRequest(ollamaApiUrl, null, ollamaModel, context).join();
|
||||
OllamaResponse ollamaResponse = gson.fromJson(responseBody, OllamaResponse.class);
|
||||
|
||||
chatHistoryManager.saveChatHistory(player.getUniqueId(), "ollama", prompt, ollamaResponse.response);
|
||||
|
||||
sendFormattedResponse(player, ollamaResponse.response);
|
||||
} catch (Exception e) {
|
||||
getLogger().severe("Error processing Ollama request: " + e.getMessage());
|
||||
@ -155,9 +213,28 @@ public class Ollamachat extends JavaPlugin implements Listener {
|
||||
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
String history = chatHistoryManager.getChatHistory(player.getUniqueId(), aiName);
|
||||
|
||||
String context = history + "User: " + prompt;
|
||||
|
||||
AIConfig aiConfig = otherAIConfigs.get(aiName);
|
||||
String responseBody = aiService.sendRequest(aiConfig.getApiUrl(), aiConfig.getApiKey(), aiConfig.getModel(), prompt).join();
|
||||
sendFormattedResponse(player, responseBody);
|
||||
String responseBody = aiService.sendRequest(
|
||||
aiConfig.getApiUrl(),
|
||||
aiConfig.getApiKey(),
|
||||
aiConfig.getModel(),
|
||||
context
|
||||
).join();
|
||||
|
||||
String response = parseAIResponse(aiName, responseBody);
|
||||
|
||||
chatHistoryManager.saveChatHistory(
|
||||
player.getUniqueId(),
|
||||
aiName,
|
||||
prompt,
|
||||
response
|
||||
);
|
||||
|
||||
sendFormattedResponse(player, response);
|
||||
} catch (Exception e) {
|
||||
getLogger().severe("Error processing " + aiName + " request: " + e.getMessage());
|
||||
sendErrorMessage(player, getMessage("error-prefix", null) + "Failed to get response from " + aiName);
|
||||
@ -165,6 +242,19 @@ public class Ollamachat extends JavaPlugin implements Listener {
|
||||
});
|
||||
}
|
||||
|
||||
private String parseAIResponse(String aiName, String responseBody) {
|
||||
switch (aiName.toLowerCase()) {
|
||||
case "openai":
|
||||
JsonObject json = gson.fromJson(responseBody, JsonObject.class);
|
||||
return json.getAsJsonArray("choices")
|
||||
.get(0).getAsJsonObject()
|
||||
.get("message").getAsJsonObject()
|
||||
.get("content").getAsString();
|
||||
default:
|
||||
return gson.fromJson(responseBody, OllamaResponse.class).response;
|
||||
}
|
||||
}
|
||||
|
||||
private void sendFormattedResponse(Player player, String response) {
|
||||
if (response.length() > maxResponseLength) {
|
||||
response = response.substring(0, maxResponseLength) + "...";
|
||||
|
@ -10,6 +10,9 @@ response-prefix: "§b[AI] §r"
|
||||
# Length
|
||||
max-response-length: 500
|
||||
|
||||
# History
|
||||
max-history: 5
|
||||
|
||||
# Language Settings
|
||||
language: "en"
|
||||
|
||||
|
@ -4,10 +4,15 @@ toggle-enabled: "§a{ai-name} is now enabled."
|
||||
toggle-disabled: "§a{ai-name} is now disabled."
|
||||
invalid-ai-name: "§cInvalid AI name. Available AIs: {ai-list}."
|
||||
usage-aichat: "§cUsage: /aichat <ai-name> <prompt>"
|
||||
usage-ollamachat: "§cUsage: /ollamachat <reload|toggle <ai-name>>"
|
||||
player-only: "§cThis command can only be used by players."
|
||||
ollama-enabled: "§aOllama is now enabled."
|
||||
ollama-disabled: "§aOllama is now disabled."
|
||||
|
||||
# Chat interaction
|
||||
response-prefix: "§b[AI] §r"
|
||||
error-prefix: "§c[Error] §r"
|
||||
error-prefix: "§c[Error] §r"
|
||||
|
||||
# Log messages
|
||||
config-invalid: "§cConfig file is invalid or incomplete. Regenerating default config..."
|
||||
config-load-failed: "§cFailed to load config file: {error}. Regenerating default config..."
|
@ -4,10 +4,15 @@ toggle-enabled: "§a{ai-name} 已启用。"
|
||||
toggle-disabled: "§a{ai-name} 已禁用。"
|
||||
invalid-ai-name: "§c无效的 AI 名称。可用的 AI: {ai-list}。"
|
||||
usage-aichat: "§c用法: /aichat <ai名称> <提示>"
|
||||
usage-ollamachat: "§c用法: /ollamachat <reload|toggle <ai名称>>"
|
||||
player-only: "§c该命令只能由玩家使用。"
|
||||
ollama-enabled: "§aOllama 已启用。"
|
||||
ollama-disabled: "§aOllama 已禁用。"
|
||||
|
||||
# 聊天交互
|
||||
response-prefix: "§b[AI] §r"
|
||||
error-prefix: "§c[错误] §r"
|
||||
error-prefix: "§c[错误] §r"
|
||||
|
||||
# 日志消息
|
||||
config-invalid: "§c配置文件无效或不完整,正在重新生成默认配置..."
|
||||
config-load-failed: "§c加载配置文件失败: {error},正在重新生成默认配置..."
|
@ -1,5 +1,5 @@
|
||||
name: ollamachat
|
||||
version: '1.0.1'
|
||||
name: OllamaChat
|
||||
version: '1.0.2'
|
||||
main: com.ollamachat.Ollamachat
|
||||
api-version: '1.21'
|
||||
authors: [xwwsdd]
|
||||
@ -9,13 +9,11 @@ website: https://chat.sarskin.cn/invite/iHgI6LTX
|
||||
commands:
|
||||
ollamachat:
|
||||
description: Manage OllamaChat plugin (reload configuration or toggle AI)
|
||||
usage: |
|
||||
/ollamachat reload - Reload the plugin configuration
|
||||
/ollamachat toggle <ai-name> - Enable or disable an AI service
|
||||
usage: "{usage-ollamachat}"
|
||||
permission: ollamachat.admin
|
||||
aichat:
|
||||
description: Interact with other AI services
|
||||
usage: /aichat <ai-name> <prompt>
|
||||
usage: "{usage-aichat}"
|
||||
permission: ollamachat.use
|
||||
|
||||
permissions:
|
||||
|
Loading…
Reference in New Issue
Block a user