forked from neurodock/NeuroDock
8e44c11385
This commit introduces a large-scale refactor that replaces the existing TCP-based API with a Spring Boot-powered HTTP REST architecture. Due to the size and scope of this change, only essential structural notes are included below. High-level changes: - Replaced TCP-based communication with RESTful endpoints - Introduced Spring Boot for API handling and configuration - Refactored internal core logic to support REST architecture New/Updated API components: - `APIApplication.java`: Main Spring Boot entry point - `MessageController.java`: Handles LLM-related queries - `ToolController.java`: Handles adding/removing tools - `NewToolRequest.java` / `NewToolResponse.java`: Data models for tool addition - `NewQueryResponseHook.java`: Webhook handler for LLM query results - `WebhookError.java`: Model for reporting webhook errors - `EnableIfNotDisplay.java`: Conditional configuration for TTY context - Other supporting classes (e.g., `ToolArgument`, `ToolRequest`) Core changes: - `Core.java`: Removed deprecated `addFunctionTool`, added `removeTool` - `LaunchOptions.java`: Added `notDisplay` flag for headless operation - `OllamaObject.java`: Implements tool removal logic Launcher/display changes: - `Launcher.java`: Starts `APIApplication` if not in TTY mode - `Display.java`: Integrates REST API contextually with TTY display NOTE: Several classes are included but not yet fully utilized; these are placeholders for upcoming features (e.g., `MessageResponse`, `ToolRequest`). BREAKING CHANGE: This refactors removes all TCP-based API code and replaces it with HTTP REST using Spring Boot. Any clients or modules depending on the old TCP interface will need to be updated.
128 lines
4.7 KiB
Java
128 lines
4.7 KiB
Java
package me.zacharias.chat.api;
|
|
|
|
import me.zacharias.chat.api.payload.request.NewQurryResponceHook;
|
|
import me.zacharias.chat.api.payload.request.NewToolRequest;
|
|
import me.zacharias.chat.core.Core;
|
|
import me.zacharias.chat.core.GlobalObjects;
|
|
import me.zacharias.chat.core.PrintMessageHandler;
|
|
import me.zacharias.chat.ollama.OllamaObject;
|
|
import org.springframework.boot.SpringApplication;
|
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
import org.springframework.context.ConfigurableApplicationContext;
|
|
import org.springframework.web.reactive.function.client.WebClient;
|
|
import reactor.core.publisher.Mono;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
|
|
@SpringBootApplication
|
|
public class APIApplication {
|
|
public static void start(){
|
|
ConfigurableApplicationContext ctx = SpringApplication.run(APIApplication.class);
|
|
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
SpringApplication.run(APIApplication.class, args);
|
|
}
|
|
|
|
private static APIApplication instance;
|
|
private final Map<String, APITool> tools = new HashMap<>();
|
|
private final ArrayList<QuerryResponceEndpoint> querryResponceEndpoints = new ArrayList<>();
|
|
private final Core core;
|
|
|
|
WebClient webClient = WebClient.create();
|
|
|
|
public APIApplication()
|
|
{
|
|
if(instance != null)
|
|
throw new IllegalStateException("APIApplication is already running!");
|
|
|
|
instance = this;
|
|
|
|
if(GlobalObjects.getObject("core") instanceof Core coreInstance) {
|
|
this.core = coreInstance;
|
|
} else {
|
|
this.core = new Core(new PrintMessageHandler() {
|
|
@Override
|
|
public void printMessage(String message) {
|
|
synchronized (querryResponceEndpoints) {
|
|
querryResponceEndpoints.forEach(endpoint -> {
|
|
webClient.post().uri(endpoint.getUrl()).bodyValue(message).retrieve()
|
|
.onStatus(
|
|
status -> status.is4xxClientError() || status.is5xxServerError() || status.isError(),
|
|
clientResponse -> {
|
|
return clientResponse.bodyToMono(String.class).flatMap(body -> {
|
|
endpoint.sendError("Failed to send message for qurry response");
|
|
querryResponceEndpoints.remove(endpoint);
|
|
return Mono.error(new RuntimeException("Webhook call failed with status: " + clientResponse.statusCode()));
|
|
});
|
|
})
|
|
.toBodilessEntity()
|
|
.doOnError(e -> {
|
|
|
|
})
|
|
.subscribe();
|
|
});
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean color() {
|
|
return false;
|
|
}
|
|
});
|
|
|
|
core.setOllamaObject(OllamaObject.builder()
|
|
.setModel("qwen3:8b")
|
|
.keep_alive(10)
|
|
.build());
|
|
}
|
|
}
|
|
|
|
public static APIApplication getInstance() {
|
|
return instance;
|
|
}
|
|
|
|
public boolean addTool(String name, NewToolRequest request) {
|
|
if (name == null || name.isEmpty()) {
|
|
throw new IllegalArgumentException("Tool name cannot be null or empty");
|
|
}
|
|
if (tools.containsKey(name)) {
|
|
return false; // Tool with this name already exists
|
|
}
|
|
|
|
APITool tool = new APITool(request);
|
|
tools.put(name, tool);
|
|
core.addTool(tool, Core.Source.API);
|
|
return true;
|
|
}
|
|
|
|
public boolean removeTool(String name) {
|
|
if(name == null || name.isEmpty()) {
|
|
throw new IllegalArgumentException("Tool name cannot be null or empty");
|
|
}
|
|
if (!tools.containsKey(name)) {
|
|
return false; // Tool with this name does not exist
|
|
}
|
|
APITool tool = tools.remove(name);
|
|
core.removeTool(tool.name());
|
|
return true;
|
|
}
|
|
|
|
public void addQurryResponseHook(NewQurryResponceHook request) {
|
|
if (request == null || request.getUrl() == null || request.getUrl().isEmpty()) {
|
|
throw new IllegalArgumentException("Request and URL cannot be null or empty");
|
|
}
|
|
QuerryResponceEndpoint endpoint = new QuerryResponceEndpoint(request.getUrl(), request.getErrorUrl());
|
|
synchronized (querryResponceEndpoints) {
|
|
querryResponceEndpoints.add(endpoint);
|
|
}
|
|
}
|
|
|
|
public Core getCore() {
|
|
return core;
|
|
}
|
|
}
|