Files
NeuroDock/API/src/main/java/me/zacharias/chat/api/APIApplication.java
T
Zacharias 8e44c11385 feat(api): major refactor from TCP socket to HTTP REST via Spring Boot
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.
2025-05-24 18:11:58 +02:00

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;
}
}