i18n: per-player EN/PT via Adventure GlobalTranslator (#1)
Foundation + commands module of the i18n spec. - I18n registry/loader + Lang.tr facade + reloadI18n - PT source-of-truth bundle + EN translation - CanalhandiaCommand player-facing strings migrated; admin-tuning/help/enum-labels deferred - I18nTest: parity + per-locale render + pt_BR fallback; 316/316 green 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit was merged in pull request #1.
This commit is contained in:
@@ -171,6 +171,88 @@ final class MiniMax {
|
||||
return content.getAsString();
|
||||
}
|
||||
|
||||
/** Runs a tool the model asked for and returns its result text. */
|
||||
@FunctionalInterface
|
||||
interface ToolExecutor {
|
||||
String run(String name, String argumentsJson);
|
||||
}
|
||||
|
||||
/**
|
||||
* Answers with tools: the model may call the given tools, whose results are
|
||||
* fed back until it produces a final answer or {@code maxCalls} rounds pass.
|
||||
*
|
||||
* <p>The last round is deliberately sent tool-free, so a model that keeps
|
||||
* asking for tools instead of answering is still forced to produce prose
|
||||
* rather than looping forever. Every failure returns null, like {@link
|
||||
* #answer}, so the caller cannot tell a broken loop from "no answer".
|
||||
*/
|
||||
String answerWithTools(String key, String model, List<Turn> initial, JsonArray tools,
|
||||
ToolExecutor executor, int maxTokens, double temperature, int maxCalls) {
|
||||
JsonArray messages = new JsonArray();
|
||||
for (Turn turn : initial) {
|
||||
JsonObject object = new JsonObject();
|
||||
object.addProperty("role", turn.role());
|
||||
object.addProperty("content", turn.content());
|
||||
messages.add(object);
|
||||
}
|
||||
for (int round = 0; round <= maxCalls; round++) {
|
||||
boolean lastRound = round == maxCalls;
|
||||
JsonObject body = new JsonObject();
|
||||
body.addProperty("model", model);
|
||||
body.add("messages", messages);
|
||||
body.addProperty("max_tokens", maxTokens);
|
||||
body.addProperty("temperature", temperature);
|
||||
if (!lastRound) {
|
||||
body.add("tools", tools);
|
||||
body.addProperty("tool_choice", "auto");
|
||||
}
|
||||
JsonObject message = message(post(key, body));
|
||||
if (message == null) {
|
||||
return null;
|
||||
}
|
||||
JsonElement calls = message.get("tool_calls");
|
||||
boolean hasCalls = calls != null && calls.isJsonArray() && !calls.getAsJsonArray().isEmpty();
|
||||
if (lastRound || !hasCalls) {
|
||||
JsonElement content = message.get("content");
|
||||
if (content != null && content.isJsonPrimitive() && !content.getAsString().isBlank()) {
|
||||
return content.getAsString();
|
||||
}
|
||||
if (lastRound) {
|
||||
warn.accept("IA: sem resposta após " + maxCalls + " rodadas de ferramenta.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// Append the assistant turn (carrying its tool_calls) verbatim, then
|
||||
// one tool result per call. Some servers reject a null content on an
|
||||
// assistant turn, so an empty string stands in.
|
||||
JsonObject assistant = message.deepCopy();
|
||||
if (!assistant.has("content") || assistant.get("content").isJsonNull()) {
|
||||
assistant.addProperty("content", "");
|
||||
}
|
||||
messages.add(assistant);
|
||||
for (JsonElement element : calls.getAsJsonArray()) {
|
||||
JsonObject call = element.getAsJsonObject();
|
||||
String id = call.has("id") ? call.get("id").getAsString() : "";
|
||||
JsonObject function = call.getAsJsonObject("function");
|
||||
String name = function.get("name").getAsString();
|
||||
String arguments = function.has("arguments")
|
||||
? function.get("arguments").getAsString() : "{}";
|
||||
String result;
|
||||
try {
|
||||
result = executor.run(name, arguments);
|
||||
} catch (RuntimeException e) {
|
||||
result = "erro ao executar " + name + ": " + e.getMessage();
|
||||
}
|
||||
JsonObject toolMessage = new JsonObject();
|
||||
toolMessage.addProperty("role", "tool");
|
||||
toolMessage.addProperty("tool_call_id", id);
|
||||
toolMessage.addProperty("content", result == null ? "sem resultado." : result);
|
||||
messages.add(toolMessage);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private JsonObject base(String model, List<Turn> messages, int maxTokens, double temperature) {
|
||||
JsonArray array = new JsonArray();
|
||||
for (Turn msg : messages) {
|
||||
|
||||
Reference in New Issue
Block a user