mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-11 02:37:06 +00:00
feat: mcp-1.0.0
This commit is contained in:
@@ -320,19 +320,4 @@ wechat:
|
|||||||
token: ''
|
token: ''
|
||||||
aesKey: ''
|
aesKey: ''
|
||||||
|
|
||||||
spring:
|
|
||||||
ai:
|
|
||||||
openai:
|
|
||||||
api-key: sk-xX
|
|
||||||
base-url: https://api.pandarobot.chat/
|
|
||||||
ollama:
|
|
||||||
base-url: http://localhost:11434
|
|
||||||
mcp:
|
|
||||||
client:
|
|
||||||
enabled: true
|
|
||||||
name: call-mcp-server
|
|
||||||
sse:
|
|
||||||
connections:
|
|
||||||
server1:
|
|
||||||
url: http://127.0.0.1:6040
|
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,11 @@ public class ChatRequest {
|
|||||||
*/
|
*/
|
||||||
private Boolean search = Boolean.FALSE;
|
private Boolean search = Boolean.FALSE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否开启mcp
|
||||||
|
*/
|
||||||
|
private Boolean isMcp = Boolean.FALSE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 知识库id
|
* 知识库id
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-ai</artifactId>
|
|
||||||
<version>1.0.0</version>
|
|
||||||
<relativePath>../../pom.xml</relativePath>
|
|
||||||
</parent>
|
|
||||||
<artifactId>call-mcp-server</artifactId>
|
|
||||||
<name>Archetype - call-mcp-server</name>
|
|
||||||
<url>http://maven.apache.org</url>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<java.version>17</java.version>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
|
|
||||||
<dependencyManagement>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.ai</groupId>
|
|
||||||
<artifactId>spring-ai-bom</artifactId>
|
|
||||||
<version>1.0.0-M6</version>
|
|
||||||
<type>pom</type>
|
|
||||||
<scope>import</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
</dependencyManagement>
|
|
||||||
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.ai</groupId>
|
|
||||||
<artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
|
|
||||||
<version>1.0.0-M6</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.ai</groupId>
|
|
||||||
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.ai</groupId>
|
|
||||||
<artifactId>spring-ai-mcp</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
</project>
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package org.ruoyi.rocket.callmcpserver;
|
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
||||||
|
|
||||||
@SpringBootApplication
|
|
||||||
public class CallMcpServerApplication {
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
SpringApplication.run(CallMcpServerApplication.class, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package org.ruoyi.rocket.callmcpserver.cofing;
|
|
||||||
|
|
||||||
import io.modelcontextprotocol.client.McpClient;
|
|
||||||
import org.springframework.ai.mcp.customizer.McpSyncClientCustomizer;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ageer
|
|
||||||
*/
|
|
||||||
@Configuration
|
|
||||||
public class McpClientCfg implements McpSyncClientCustomizer {
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void customize(String name, McpClient.SyncSpec spec) {
|
|
||||||
// do nothing
|
|
||||||
spec.requestTimeout(Duration.ofSeconds(30));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
package org.ruoyi.rocket.callmcpserver.view;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import org.springframework.ai.chat.client.ChatClient;
|
|
||||||
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
|
|
||||||
import org.springframework.ai.chat.memory.ChatMemory;
|
|
||||||
import org.springframework.ai.chat.memory.InMemoryChatMemory;
|
|
||||||
import org.springframework.ai.chat.model.ChatResponse;
|
|
||||||
import org.springframework.ai.openai.OpenAiChatOptions;
|
|
||||||
import org.springframework.ai.tool.ToolCallbackProvider;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author jianzhang
|
|
||||||
* 2025/03/18/下午8:00
|
|
||||||
*/
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/dashscope/chat-client")
|
|
||||||
public class ChatController {
|
|
||||||
|
|
||||||
private final ChatClient chatClient;
|
|
||||||
|
|
||||||
private final ChatMemory chatMemory = new InMemoryChatMemory();
|
|
||||||
|
|
||||||
public ChatController(ChatClient.Builder chatClientBuilder,ToolCallbackProvider tools) {
|
|
||||||
this.chatClient = chatClientBuilder
|
|
||||||
.defaultTools(tools)
|
|
||||||
.defaultOptions(
|
|
||||||
OpenAiChatOptions.builder().model("gpt-4o-mini").build())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequestMapping(value = "/generate_stream", method = RequestMethod.GET)
|
|
||||||
public Flux<ChatResponse> generateStream(HttpServletResponse response, @RequestParam("id") String id, @RequestParam("prompt") String prompt) {
|
|
||||||
response.setCharacterEncoding("UTF-8");
|
|
||||||
var messageChatMemoryAdvisor = new MessageChatMemoryAdvisor(chatMemory, id, 10);
|
|
||||||
|
|
||||||
|
|
||||||
Flux<ChatResponse> chatResponseFlux = this.chatClient.prompt(prompt)
|
|
||||||
.advisors(messageChatMemoryAdvisor)
|
|
||||||
.stream()
|
|
||||||
.chatResponse();
|
|
||||||
|
|
||||||
Flux<String> content = this.chatClient.prompt(prompt)
|
|
||||||
.advisors(messageChatMemoryAdvisor)
|
|
||||||
.stream()
|
|
||||||
.content();
|
|
||||||
|
|
||||||
content.subscribe(
|
|
||||||
content1 -> System.out.println("chatResponse"+content1)
|
|
||||||
);
|
|
||||||
return chatResponseFlux;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@GetMapping("/advisor/chat/{id}/{prompt}")
|
|
||||||
public Flux<String> advisorChat(
|
|
||||||
HttpServletResponse response,
|
|
||||||
@PathVariable String id,
|
|
||||||
@PathVariable String prompt) {
|
|
||||||
|
|
||||||
response.setCharacterEncoding("UTF-8");
|
|
||||||
var messageChatMemoryAdvisor = new MessageChatMemoryAdvisor(chatMemory, id, 10);
|
|
||||||
return this.chatClient.prompt(prompt)
|
|
||||||
.advisors(messageChatMemoryAdvisor).stream().content();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package org.ruoyi.rocket.callmcpserver.view;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.ui.Model;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author jianzhang
|
|
||||||
* 2025/03/18/下午8:00
|
|
||||||
*/
|
|
||||||
@Controller
|
|
||||||
public class IndexController {
|
|
||||||
|
|
||||||
@GetMapping("/")
|
|
||||||
public String chat(Model model) {
|
|
||||||
//model.addAttribute("name", "User");
|
|
||||||
// 返回视图名称,对应 templates/index.html
|
|
||||||
return "index";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
server:
|
|
||||||
port: 9999
|
|
||||||
spring:
|
|
||||||
ai:
|
|
||||||
openai:
|
|
||||||
api-key: sk-xXe1WMPjhlVb1aiI1b4c6c8934D8463f9e4b67Ed8718B772
|
|
||||||
base-url: https://api.pandarobot.chat/
|
|
||||||
mcp:
|
|
||||||
client:
|
|
||||||
enabled: true
|
|
||||||
name: call-mcp-server
|
|
||||||
sse:
|
|
||||||
connections:
|
|
||||||
server1:
|
|
||||||
url: http://127.0.0.1:6040
|
|
||||||
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"mcpServers": {
|
|
||||||
"fileSystem": {
|
|
||||||
"command": "D:\\software\\nodeJs\\npx.cmd",
|
|
||||||
"args": [
|
|
||||||
"-y",
|
|
||||||
"@modelcontextprotocol/server-filesystem",
|
|
||||||
"D:\\software\\sqlite"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"sqlLite": {
|
|
||||||
"command": "D:\\Program Files\\python3.12.3\\Scripts\\uvx.exe",
|
|
||||||
"args": [
|
|
||||||
"mcp-server-sqlite",
|
|
||||||
"--db-path",
|
|
||||||
"D:\\work-space-study\\spring-ai-mcp-demo\\mcp-client\\src\\main\\resources\\test.db"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"fetch": {
|
|
||||||
"command": "D:\\Program Files\\python3.12.3\\Scripts\\uvx.exe",
|
|
||||||
"args": [
|
|
||||||
"mcp-server-fetch"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"baidu-map": {
|
|
||||||
"command": "D:\\Program Files\\python3.12.3\\Scripts\\uvx.exe",
|
|
||||||
"args": [
|
|
||||||
"run",
|
|
||||||
"--with",
|
|
||||||
"mcp[cli]",
|
|
||||||
"mcp",
|
|
||||||
"run",
|
|
||||||
"D:\\work-space-python\\python-baidu-map\\baidu_map_mcp_server\\map.py"
|
|
||||||
],
|
|
||||||
"env": {
|
|
||||||
"BAIDU_MAPS_API_KEY": "{百度地图API-KEY}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"mcpServers": {
|
|
||||||
"fileSystem": {
|
|
||||||
"command": "D:\\software\\nodeJs\\npx.cmd",
|
|
||||||
"args": [
|
|
||||||
"-y",
|
|
||||||
"@modelcontextprotocol/server-filesystem",
|
|
||||||
"D:\\software\\sqlite"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"sqlLite": {
|
|
||||||
"command": "D:\\Program Files\\python3.12.3\\Scripts\\uvx.exe",
|
|
||||||
"args": [
|
|
||||||
"mcp-server-sqlite",
|
|
||||||
"--db-path",
|
|
||||||
"D:\\work-space-study\\spring-ai-mcp-demo\\mcp-client\\src\\main\\resources\\test.db"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>AI 对话助手</title>
|
|
||||||
<script src="https://cdn.tailwindcss.com"></script>
|
|
||||||
</head>
|
|
||||||
<body class="bg-gray-100 min-h-screen">
|
|
||||||
<div class="container mx-auto p-4 max-w-3xl">
|
|
||||||
<!-- 标题 -->
|
|
||||||
<div class="text-center mb-8">
|
|
||||||
<h1 class="text-3xl font-bold text-gray-800">AI 对话助手</h1>
|
|
||||||
<p class="text-gray-600 mt-2">基于 Spring AI 的流式对话系统 By AhuCodingBeast</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 聊天容器 -->
|
|
||||||
<div id="chat-container" class="bg-white rounded-xl shadow-lg p-4 mb-4 h-[500px] overflow-y-auto space-y-4">
|
|
||||||
<!-- 初始欢迎消息 -->
|
|
||||||
<div class="ai-message flex items-start gap-3">
|
|
||||||
<div class="bg-green-100 p-3 rounded-lg max-w-[85%]">
|
|
||||||
<span class="text-gray-800">您好!我是AI助手,有什么可以帮您?</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 输入区域 -->
|
|
||||||
<div class="flex gap-2">
|
|
||||||
<input type="text" id="message-input"
|
|
||||||
class="flex-1 border border-gray-300 rounded-xl px-4 py-3 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
placeholder="输入您的问题...">
|
|
||||||
<button id="send-button"
|
|
||||||
class="bg-blue-500 text-white px-6 py-3 rounded-xl hover:bg-blue-600 transition-colors flex items-center">
|
|
||||||
<span>发送</span>
|
|
||||||
<svg id="loading-spinner" class="hidden w-4 h-4 ml-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
|
||||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
||||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const chatContainer = document.getElementById('chat-container');
|
|
||||||
const messageInput = document.getElementById('message-input');
|
|
||||||
const sendButton = document.getElementById('send-button');
|
|
||||||
const loadingSpinner = document.getElementById('loading-spinner');
|
|
||||||
|
|
||||||
// 发送消息处理
|
|
||||||
function handleSend() {
|
|
||||||
const message = messageInput.value.trim();
|
|
||||||
if (!message) return;
|
|
||||||
|
|
||||||
// 添加用户消息
|
|
||||||
addMessage(message, 'user');
|
|
||||||
messageInput.value = '';
|
|
||||||
|
|
||||||
// 构建API URL
|
|
||||||
const apiUrl = new URL('http://localhost:9999/dashscope/chat-client/generate_stream');
|
|
||||||
apiUrl.searchParams.append('id', '01');
|
|
||||||
apiUrl.searchParams.append('prompt', message);
|
|
||||||
|
|
||||||
// 显示加载状态
|
|
||||||
sendButton.disabled = true;
|
|
||||||
loadingSpinner.classList.remove('hidden');
|
|
||||||
|
|
||||||
// 创建EventSource连接
|
|
||||||
const eventSource = new EventSource(apiUrl);
|
|
||||||
let aiMessageElement = null;
|
|
||||||
|
|
||||||
eventSource.onmessage = (event) => {
|
|
||||||
try {
|
|
||||||
const data = JSON.parse(event.data);
|
|
||||||
console.log(data);
|
|
||||||
const content = data.result?.output?.text || '';
|
|
||||||
const finishReason = data.result?.metadata?.finishReason;
|
|
||||||
|
|
||||||
// 创建消息容器(如果不存在)
|
|
||||||
if (!aiMessageElement) {
|
|
||||||
aiMessageElement = addMessage('', 'ai');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 追加内容
|
|
||||||
if (content) {
|
|
||||||
aiMessageElement.querySelector('.message-content').textContent += content;
|
|
||||||
autoScroll();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理结束
|
|
||||||
if (finishReason === 'STOP') {
|
|
||||||
eventSource.close();
|
|
||||||
sendButton.disabled = false;
|
|
||||||
loadingSpinner.classList.add('hidden');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('解析错误:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
eventSource.onerror = (error) => {
|
|
||||||
console.error('连接错误:', error);
|
|
||||||
eventSource.close();
|
|
||||||
sendButton.disabled = false;
|
|
||||||
loadingSpinner.classList.add('hidden');
|
|
||||||
addMessage('对话连接异常,请重试', 'ai', true);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加消息到容器
|
|
||||||
function addMessage(content, type, isError = false) {
|
|
||||||
const messageDiv = document.createElement('div');
|
|
||||||
messageDiv.className = `${type}-message flex items-start gap-3`;
|
|
||||||
|
|
||||||
const bubble = document.createElement('div');
|
|
||||||
bubble.className = `p-3 rounded-lg max-w-[85%] ${
|
|
||||||
type === 'user'
|
|
||||||
? 'bg-blue-500 text-white ml-auto'
|
|
||||||
: `bg-green-100 ${isError ? 'text-red-500' : 'text-gray-800'}`
|
|
||||||
}`;
|
|
||||||
|
|
||||||
const contentSpan = document.createElement('span');
|
|
||||||
contentSpan.className = 'message-content';
|
|
||||||
contentSpan.textContent = content;
|
|
||||||
|
|
||||||
bubble.appendChild(contentSpan);
|
|
||||||
messageDiv.appendChild(bubble);
|
|
||||||
chatContainer.appendChild(messageDiv);
|
|
||||||
|
|
||||||
autoScroll();
|
|
||||||
return bubble;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 自动滚动到底部
|
|
||||||
function autoScroll() {
|
|
||||||
chatContainer.scrollTop = chatContainer.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 事件监听
|
|
||||||
sendButton.addEventListener('click', handleSend);
|
|
||||||
messageInput.addEventListener('keypress', (e) => {
|
|
||||||
if (e.key === 'Enter' && !e.shiftKey) {
|
|
||||||
e.preventDefault();
|
|
||||||
handleSend();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>ruoyi-mcp-server</module>
|
<module>ruoyi-ai-mcp-webflux-server</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
91
ruoyi-extend/ruoyi-ai-mcp-webflux-server/pom.xml
Normal file
91
ruoyi-extend/ruoyi-ai-mcp-webflux-server/pom.xml
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>3.4.4</version>
|
||||||
|
<relativePath /> <!-- lookup parent from repository -->
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<groupId>com.ivy.mcp</groupId>
|
||||||
|
<artifactId>ruoyi-ai-mcp-webflux-server</artifactId>
|
||||||
|
<version>1.0.0-M6</version>
|
||||||
|
|
||||||
|
<name>spring-ai-mcp-webflux-server</name>
|
||||||
|
<description>Spring AI MCP Server example and invoke by stdio</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>17</java.version>
|
||||||
|
<spring-ai.version>1.0.0-M6</spring-ai.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.ai</groupId>
|
||||||
|
<artifactId>spring-ai-bom</artifactId>
|
||||||
|
<version>${spring-ai.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.ai</groupId>
|
||||||
|
<artifactId>spring-ai-mcp-server-webflux-spring-boot-starter</artifactId>
|
||||||
|
<version>${spring-ai.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>repackage</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<name>Central Portal Snapshots</name>
|
||||||
|
<id>central-portal-snapshots</id>
|
||||||
|
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
|
||||||
|
<releases>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</releases>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>spring-milestones</id>
|
||||||
|
<name>Spring Milestones</name>
|
||||||
|
<url>https://repo.spring.io/milestone</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>spring-snapshots</id>
|
||||||
|
<name>Spring Snapshots</name>
|
||||||
|
<url>https://repo.spring.io/snapshot</url>
|
||||||
|
<releases>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</releases>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.ivy.mcp.sse.client;
|
||||||
|
|
||||||
|
|
||||||
|
import io.modelcontextprotocol.client.McpClient;
|
||||||
|
import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport;
|
||||||
|
import io.modelcontextprotocol.spec.McpSchema;
|
||||||
|
import org.springframework.web.reactive.function.client.WebClient;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ClientWebflux {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
var transport = new WebFluxSseClientTransport(WebClient.builder().baseUrl("http://localhost:8080"));
|
||||||
|
try (var client = McpClient.sync(transport).build()) {
|
||||||
|
|
||||||
|
client.initialize();
|
||||||
|
// client.ping();
|
||||||
|
|
||||||
|
McpSchema.ListToolsResult toolsList = client.listTools();
|
||||||
|
System.out.println("Available Tools = " + toolsList);
|
||||||
|
|
||||||
|
McpSchema.CallToolResult sumResult = client.callTool(new McpSchema.CallToolRequest("add",
|
||||||
|
Map.of("a", 1, "b", 2)));
|
||||||
|
System.out.println("add a+ b = " + sumResult.content().get(0));
|
||||||
|
|
||||||
|
|
||||||
|
McpSchema.CallToolResult currentTimResult = client.callTool(new McpSchema.CallToolRequest("getCurrentTime", Map.of()));
|
||||||
|
System.out.println("current time Response = " + currentTimResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.ivy.mcp.sse.server;
|
||||||
|
|
||||||
|
import org.springframework.ai.tool.ToolCallback;
|
||||||
|
import org.springframework.ai.tool.ToolCallbacks;
|
||||||
|
import org.springframework.ai.tool.annotation.Tool;
|
||||||
|
import org.springframework.ai.tool.annotation.ToolParam;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class McpWebfluxServerApplication {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(McpWebfluxServerApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public List<ToolCallback> tools(MyTools myTools) {
|
||||||
|
return List.of(ToolCallbacks.from(myTools));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public static class MyTools {
|
||||||
|
|
||||||
|
@Tool(description = "add two numbers")
|
||||||
|
public Integer add(@ToolParam(description = "first number") int a,
|
||||||
|
@ToolParam(description = "second number") int b) {
|
||||||
|
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Tool(description = "get current time")
|
||||||
|
public LocalDateTime getCurrentTime() {
|
||||||
|
return LocalDateTime.now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
spring.main.banner-mode=off
|
||||||
|
logging.pattern.console=
|
||||||
|
logging.file.name=mcp-server/spring-ai-mcp-webflux-server/target/target/mcp.mytools.log
|
||||||
|
|
||||||
|
spring.ai.mcp.server.enabled=true
|
||||||
|
spring.ai.mcp.server.name=webflux-server
|
||||||
|
spring.ai.mcp.server.version=1.0.0
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-ai</artifactId>
|
|
||||||
<version>${revision}</version>
|
|
||||||
<relativePath>../../pom.xml</relativePath>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<artifactId>ruoyi-mcp-server</artifactId>
|
|
||||||
|
|
||||||
<dependencyManagement>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.ai</groupId>
|
|
||||||
<artifactId>spring-ai-bom</artifactId>
|
|
||||||
<version>1.0.0-M6</version>
|
|
||||||
<type>pom</type>
|
|
||||||
<scope>import</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
</dependencyManagement>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.ai</groupId>
|
|
||||||
<artifactId>spring-ai-mcp-server-webmvc-spring-boot-starter</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.ai</groupId>
|
|
||||||
<artifactId>spring-ai-mcp</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- <dependency>-->
|
|
||||||
<!-- <groupId>org.ruoyi</groupId>-->
|
|
||||||
<!-- <artifactId>ruoyi-system-api</artifactId>-->
|
|
||||||
<!-- <exclusions>-->
|
|
||||||
<!-- <exclusion>-->
|
|
||||||
<!-- <groupId>org.ruoyi</groupId>-->
|
|
||||||
<!-- <artifactId>ruoyi-common-translation</artifactId>-->
|
|
||||||
<!-- </exclusion>-->
|
|
||||||
<!-- </exclusions>-->
|
|
||||||
<!-- </dependency>-->
|
|
||||||
|
|
||||||
</dependencies>
|
|
||||||
</project>
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package org.ruoyi.mcp;
|
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ageer
|
|
||||||
*/
|
|
||||||
@SpringBootApplication
|
|
||||||
public class McpServerApplication {
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
SpringApplication.run(McpServerApplication.class, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
package org.ruoyi.mcp.config;
|
|
||||||
|
|
||||||
import org.ruoyi.mcp.service.McpCustomService;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import io.modelcontextprotocol.server.McpServerFeatures;
|
|
||||||
import io.modelcontextprotocol.spec.McpSchema;
|
|
||||||
import org.springframework.ai.tool.ToolCallbackProvider;
|
|
||||||
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ageer
|
|
||||||
*/
|
|
||||||
@Configuration
|
|
||||||
@EnableWebMvc
|
|
||||||
public class McpServerConfig implements WebMvcConfigurer {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ToolCallbackProvider openLibraryTools(McpCustomService mcpService) {
|
|
||||||
return MethodToolCallbackProvider.builder().toolObjects(mcpService).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public List<McpServerFeatures.SyncResourceRegistration> resourceRegistrations() {
|
|
||||||
|
|
||||||
// Create a resource registration for system information
|
|
||||||
var systemInfoResource = new McpSchema.Resource(
|
|
||||||
"system://info",
|
|
||||||
"System Information",
|
|
||||||
"Provides basic system information including Java version, OS, etc.",
|
|
||||||
"application/json", null
|
|
||||||
);
|
|
||||||
|
|
||||||
var resourceRegistration = new McpServerFeatures.SyncResourceRegistration(systemInfoResource, (request) -> {
|
|
||||||
try {
|
|
||||||
var systemInfo = Map.of(
|
|
||||||
"javaVersion", System.getProperty("java.version"),
|
|
||||||
"osName", System.getProperty("os.name"),
|
|
||||||
"osVersion", System.getProperty("os.version"),
|
|
||||||
"osArch", System.getProperty("os.arch"),
|
|
||||||
"processors", Runtime.getRuntime().availableProcessors(),
|
|
||||||
"timestamp", System.currentTimeMillis());
|
|
||||||
|
|
||||||
String jsonContent = new ObjectMapper().writeValueAsString(systemInfo);
|
|
||||||
|
|
||||||
return new McpSchema.ReadResourceResult(
|
|
||||||
List.of(new McpSchema.TextResourceContents(request.uri(), "application/json", jsonContent)));
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
throw new RuntimeException("Failed to generate system info", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return List.of(resourceRegistration);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public List<McpServerFeatures.SyncPromptRegistration> promptRegistrations() {
|
|
||||||
|
|
||||||
var prompt = new McpSchema.Prompt("greeting", "A friendly greeting prompt",
|
|
||||||
List.of(new McpSchema.PromptArgument("name", "The name to greet", true)));
|
|
||||||
|
|
||||||
var promptRegistration = new McpServerFeatures.SyncPromptRegistration(prompt, getPromptRequest -> {
|
|
||||||
|
|
||||||
String nameArgument = (String) getPromptRequest.arguments().get("name");
|
|
||||||
if (nameArgument == null) {
|
|
||||||
nameArgument = "friend";
|
|
||||||
}
|
|
||||||
|
|
||||||
var userMessage = new McpSchema.PromptMessage(McpSchema.Role.USER,
|
|
||||||
new McpSchema.TextContent("Hello " + nameArgument + "! How can I assist you today?"));
|
|
||||||
|
|
||||||
return new McpSchema.GetPromptResult("A personalized greeting message", List.of(userMessage));
|
|
||||||
});
|
|
||||||
|
|
||||||
return List.of(promptRegistration);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public Consumer<List<McpSchema.Root>> rootsChangeConsumer() {
|
|
||||||
return roots -> {
|
|
||||||
System.out.println("rootsChange");
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package org.ruoyi.mcp.service;
|
|
||||||
|
|
||||||
import org.springframework.ai.tool.annotation.Tool;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ageer
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
public class McpCustomService {
|
|
||||||
|
|
||||||
public record User(String userName, String userBalance) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Tool(description = "根据用户名称查询用户信息")
|
|
||||||
public User getUserBalance(String username) {
|
|
||||||
return new User("admin","99.99");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
server:
|
|
||||||
port: 6040
|
|
||||||
spring:
|
|
||||||
application:
|
|
||||||
name: mcp-server
|
|
||||||
ai:
|
|
||||||
mcp:
|
|
||||||
server:
|
|
||||||
name: webmvc-mcp-server
|
|
||||||
version: 1.0.0
|
|
||||||
type: SYNC
|
|
||||||
sse-message-endpoint: /mcp/messages
|
|
||||||
@@ -55,19 +55,17 @@
|
|||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.ai</groupId>
|
<groupId>org.springframework.ai</groupId>
|
||||||
<artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
|
<artifactId>spring-ai-mcp-client-webflux-spring-boot-starter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- <dependency>-->
|
|
||||||
<!-- <groupId>org.springframework.ai</groupId>-->
|
|
||||||
<!-- <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>-->
|
|
||||||
<!-- </dependency>-->
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.ai</groupId>
|
<groupId>io.modelcontextprotocol.sdk</groupId>
|
||||||
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
|
<artifactId>mcp-spring-webflux</artifactId>
|
||||||
|
<version>0.8.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public class ChatConfig {
|
|||||||
return openAiStreamClient;
|
return openAiStreamClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OpenAiStreamClient createOpenAiStreamClient(String apiHost, String apiKey) {
|
public static OpenAiStreamClient createOpenAiStreamClient(String apiHost, String apiKey) {
|
||||||
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(new OpenAILogger());
|
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(new OpenAILogger());
|
||||||
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
|
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
|
||||||
OkHttpClient okHttpClient = new OkHttpClient.Builder()
|
OkHttpClient okHttpClient = new OkHttpClient.Builder()
|
||||||
|
|||||||
@@ -18,9 +18,4 @@ public interface IChatService {
|
|||||||
SseEmitter chat(ChatRequest chatRequest,SseEmitter emitter);
|
SseEmitter chat(ChatRequest chatRequest,SseEmitter emitter);
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 客户端发送消息到服务端
|
|
||||||
* @param chatRequest 请求对象
|
|
||||||
*/
|
|
||||||
void mcpChat(ChatRequest chatRequest,SseEmitter emitter);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,11 +55,4 @@ public interface ISseService {
|
|||||||
*/
|
*/
|
||||||
String wxCpChat(String prompt);
|
String wxCpChat(String prompt);
|
||||||
|
|
||||||
/**
|
|
||||||
* 联网查询
|
|
||||||
*
|
|
||||||
* @param prompt 提示词
|
|
||||||
* @return 查询内容
|
|
||||||
*/
|
|
||||||
String webSearch (String prompt);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,78 +1,88 @@
|
|||||||
package org.ruoyi.chat.service.chat.impl;
|
package org.ruoyi.chat.service.chat.impl;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import io.modelcontextprotocol.client.McpClient;
|
||||||
|
import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport;
|
||||||
|
import io.modelcontextprotocol.spec.McpSchema;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.ruoyi.chat.service.chat.IChatService;
|
import org.ruoyi.chat.service.chat.IChatService;
|
||||||
import org.ruoyi.common.chat.entity.chat.Message;
|
import org.ruoyi.common.chat.entity.chat.ChatChoice;
|
||||||
|
import org.ruoyi.common.chat.entity.chat.ChatCompletion;
|
||||||
|
import org.ruoyi.common.chat.entity.chat.ChatCompletionResponse;
|
||||||
|
import org.ruoyi.common.chat.entity.chat.Parameters;
|
||||||
|
import org.ruoyi.common.chat.entity.chat.tool.ToolCallFunction;
|
||||||
|
import org.ruoyi.common.chat.entity.chat.tool.Tools;
|
||||||
|
import org.ruoyi.common.chat.entity.chat.tool.ToolsFunction;
|
||||||
|
import org.ruoyi.common.chat.openai.OpenAiStreamClient;
|
||||||
import org.ruoyi.common.chat.request.ChatRequest;
|
import org.ruoyi.common.chat.request.ChatRequest;
|
||||||
import org.springframework.ai.chat.client.ChatClient;
|
|
||||||
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
|
|
||||||
import org.springframework.ai.chat.memory.ChatMemory;
|
|
||||||
import org.springframework.ai.chat.memory.InMemoryChatMemory;
|
|
||||||
import org.springframework.ai.chat.messages.UserMessage;
|
|
||||||
import org.springframework.ai.openai.OpenAiChatOptions;
|
|
||||||
import org.springframework.ai.tool.ToolCallbackProvider;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.reactive.function.client.WebClient;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
import reactor.core.scheduler.Schedulers;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class OpenAIServiceImpl implements IChatService {
|
public class OpenAIServiceImpl implements IChatService {
|
||||||
|
|
||||||
private final ChatClient chatClient;
|
private final OpenAiStreamClient openAiStreamClient;
|
||||||
|
|
||||||
private final ChatMemory chatMemory = new InMemoryChatMemory();
|
|
||||||
|
|
||||||
|
|
||||||
public OpenAIServiceImpl(ChatClient.Builder chatClientBuilder, ToolCallbackProvider tools) {
|
|
||||||
this.chatClient = chatClientBuilder
|
|
||||||
.defaultTools(tools)
|
|
||||||
.defaultOptions(
|
|
||||||
OpenAiChatOptions.builder()
|
|
||||||
.model("gpt-4o-mini")
|
|
||||||
.temperature(0.4)
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SseEmitter chat(ChatRequest chatRequest,SseEmitter emitter) {
|
public SseEmitter chat(ChatRequest chatRequest,SseEmitter emitter) {
|
||||||
return emitter;
|
WebFluxSseClientTransport webFluxSseClientTransport = new WebFluxSseClientTransport(WebClient.builder().baseUrl("http://localhost:8080"));
|
||||||
}
|
ChatCompletion completion = ChatCompletion
|
||||||
|
.builder()
|
||||||
|
.messages(chatRequest.getMessages())
|
||||||
|
.model(chatRequest.getModel())
|
||||||
|
.stream(false)
|
||||||
|
.build();
|
||||||
|
List<Tools> tools = new ArrayList<>();
|
||||||
|
try (var client = McpClient.sync(webFluxSseClientTransport).build()) {
|
||||||
|
client.initialize();
|
||||||
|
McpSchema.ListToolsResult toolsList = client.listTools();
|
||||||
|
|
||||||
@Override
|
for (McpSchema.Tool mcpTool : toolsList.tools()) {
|
||||||
public void mcpChat(ChatRequest chatRequest, SseEmitter emitter) {
|
|
||||||
List<Message> msgList = chatRequest.getMessages();
|
McpSchema.JsonSchema jsonSchema = mcpTool.inputSchema();
|
||||||
// 添加记忆
|
|
||||||
for (int i = 0; i < msgList.size(); i++) {
|
Parameters parameters = Parameters.builder()
|
||||||
org.springframework.ai.chat.messages.Message springAiMessage = new UserMessage(msgList.get(i).getContent().toString());
|
.type(jsonSchema.type())
|
||||||
chatMemory.add(String.valueOf(i), springAiMessage);
|
.properties(jsonSchema.properties())
|
||||||
|
.required(jsonSchema.required()).build();
|
||||||
|
|
||||||
|
Tools tool = Tools.builder()
|
||||||
|
.type(Tools.Type.FUNCTION.getName())
|
||||||
|
.function(ToolsFunction.builder().name(mcpTool.name()).description(mcpTool.description()).parameters(parameters).build())
|
||||||
|
.build();
|
||||||
|
tools.add(tool);
|
||||||
|
}
|
||||||
|
|
||||||
|
completion.setTools(tools);
|
||||||
|
ChatCompletionResponse chatCompletionResponse = openAiStreamClient.chatCompletion(completion);
|
||||||
|
String arguments = chatCompletionResponse.getChoices().get(0).getMessage().getToolCalls().get(0).getFunction().getArguments();
|
||||||
|
String name = chatCompletionResponse.getChoices().get(0).getMessage().getToolCalls().get(0).getFunction().getName();
|
||||||
|
Map<String, Object> map = JSONUtil.toBean(arguments, Map.class);
|
||||||
|
McpSchema.CallToolResult sumResult = client.callTool(new McpSchema.CallToolRequest(name, map));
|
||||||
|
System.out.println("add a+ b = " + sumResult.content().get(0));
|
||||||
|
|
||||||
|
|
||||||
|
McpSchema.Content content = sumResult.content().get(0);
|
||||||
|
|
||||||
|
emitter.send(sumResult.content().get(0));
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
emitter.completeWithError(e);
|
||||||
}
|
}
|
||||||
var messageChatMemoryAdvisor = new MessageChatMemoryAdvisor(chatMemory, chatRequest.getUserId().toString(), 10);
|
emitter.complete();
|
||||||
|
return emitter;
|
||||||
|
|
||||||
Flux<String> content = chatClient
|
|
||||||
.prompt(chatRequest.getPrompt())
|
|
||||||
.advisors(messageChatMemoryAdvisor)
|
|
||||||
.stream().content();
|
|
||||||
|
|
||||||
content.publishOn(Schedulers.boundedElastic())
|
|
||||||
.doOnNext(text -> {
|
|
||||||
try {
|
|
||||||
emitter.send(text);
|
|
||||||
} catch (IOException e) {
|
|
||||||
emitter.completeWithError(e);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.doOnError(error -> {
|
|
||||||
log.error("Error in SSE stream: ", error);
|
|
||||||
emitter.completeWithError(error);
|
|
||||||
})
|
|
||||||
.doOnComplete(emitter::complete)
|
|
||||||
.subscribe();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user