mirror of
https://github.com/jie65535/JChatGPT.git
synced 2025-10-20 17:13:37 +08:00
Compare commits
6 Commits
eda932b4e9
...
e79bcd9983
Author | SHA1 | Date | |
---|---|---|---|
e79bcd9983 | |||
b6aa638b1a | |||
bc2eb437e6 | |||
b534606bb0 | |||
867d9ad56f | |||
92a6879cc1 |
@@ -344,8 +344,7 @@ object JChatGPT : KotlinPlugin(
|
||||
val toolCallTasks = mutableListOf<Deferred<ChatMessage>>()
|
||||
// 处理聊天流式响应
|
||||
responseFlow.collect { chunk ->
|
||||
val delta = chunk.choices[0].delta
|
||||
if (delta == null) return@collect
|
||||
val delta = chunk.choices[0].delta ?: return@collect
|
||||
|
||||
// 处理内容更新
|
||||
if (delta.content != null) {
|
||||
@@ -408,6 +407,7 @@ object JChatGPT : KotlinPlugin(
|
||||
|
||||
// 移除思考内容
|
||||
val responseContent = responseMessageBuilder?.replace(thinkRegex, "")?.trim()
|
||||
logger.info("LLM Response: $responseContent")
|
||||
// 记录AI回答
|
||||
history.add(ChatMessage.Assistant(
|
||||
content = responseContent,
|
||||
@@ -440,11 +440,14 @@ object JChatGPT : KotlinPlugin(
|
||||
if (!done) {
|
||||
history.add(ChatMessage.User(
|
||||
buildString {
|
||||
append("系统提示:本次运行还剩${retry-1}轮")
|
||||
appendLine("系统提示:本次运行最多还剩${retry-1}轮。")
|
||||
appendLine("如果要多次发言,可以一次性调用多次发言工具。")
|
||||
appendLine("如果没有什么要做的,可以提前结束。")
|
||||
appendLine("当前时间:" + dateTimeFormatter.format(OffsetDateTime.now()))
|
||||
|
||||
val newMessages = getAfterHistory(startedAt, event)
|
||||
if (newMessages.isNotEmpty()) {
|
||||
append("\n以下是上次运行至今的新消息\n\n$newMessages")
|
||||
append("以下是上次运行至今的新消息\n\n$newMessages")
|
||||
}
|
||||
}
|
||||
))
|
||||
@@ -535,12 +538,12 @@ object JChatGPT : KotlinPlugin(
|
||||
// 构造消息链
|
||||
buildMessageChain {
|
||||
var index = 0
|
||||
for ((range, msg) in t.sortedBy { it.range.start }) {
|
||||
if (index < range.start) {
|
||||
append(content, index, range.start)
|
||||
for ((range, msg) in t.sortedBy { it.range.first }) {
|
||||
if (index < range.first) {
|
||||
append(content, index, range.first)
|
||||
}
|
||||
append(msg)
|
||||
index = range.endInclusive + 1
|
||||
index = range.last + 1
|
||||
}
|
||||
// 拼接后续消息
|
||||
if (index < content.length) {
|
||||
@@ -560,15 +563,15 @@ object JChatGPT : KotlinPlugin(
|
||||
// 发送组合消息
|
||||
SendCompositeMessage(),
|
||||
|
||||
// 结束循环
|
||||
StopLoopAgent(),
|
||||
|
||||
// 记忆代理
|
||||
MemoryAppend(),
|
||||
|
||||
// 记忆修改
|
||||
MemoryReplace(),
|
||||
|
||||
// 结束循环
|
||||
StopLoopAgent(),
|
||||
|
||||
// 网页搜索
|
||||
WebSearch(),
|
||||
|
||||
@@ -584,11 +587,14 @@ object JChatGPT : KotlinPlugin(
|
||||
// 视觉代理
|
||||
VisualAgent(),
|
||||
|
||||
// 图像编辑模型
|
||||
ImageEdit(),
|
||||
|
||||
// 天气服务
|
||||
WeatherService(),
|
||||
|
||||
// Epic 免费游戏
|
||||
EpicFreeGame(),
|
||||
// EpicFreeGame(),
|
||||
|
||||
// 群管代理
|
||||
GroupManageAgent(),
|
||||
@@ -666,15 +672,15 @@ object JChatGPT : KotlinPlugin(
|
||||
val receipt = if (agent.loadingMessage.isNotEmpty()) {
|
||||
event.subject.sendMessage(agent.loadingMessage)
|
||||
} else null
|
||||
// 执行函数
|
||||
val result = try {
|
||||
// 提取参数
|
||||
val args = function.argumentsAsJsonOrNull()
|
||||
logger.info("Calling ${function.name}(${args})")
|
||||
// 执行函数
|
||||
val result = try {
|
||||
agent.execute(args, event)
|
||||
} catch (e: Throwable) {
|
||||
logger.error("Failed to call ${function.name}", e)
|
||||
"工具调用失败,请尝试自行回答用户,或如实告知。"
|
||||
"工具调用失败,请尝试自行回答用户,或如实告知。\n异常信息:${e.message}"
|
||||
}
|
||||
logger.info("Result=\"$result\"")
|
||||
// 过会撤回加载消息
|
||||
|
@@ -35,6 +35,12 @@ object PluginConfig : AutoSavePluginConfig("Config") {
|
||||
@ValueDescription("视觉模型")
|
||||
var visualModel: String by value("qwen-vl-plus")
|
||||
|
||||
@ValueDescription("百炼平台API KEY")
|
||||
val dashScopeApiKey: String by value("")
|
||||
|
||||
@ValueDescription("百炼平台图片编辑模型")
|
||||
val imageEditModel: String by value("qwen-image-edit")
|
||||
|
||||
@ValueDescription("Jina API Key")
|
||||
val jinaApiKey by value("")
|
||||
|
||||
|
@@ -29,9 +29,9 @@ abstract class BaseAgent(
|
||||
protected val httpClient by lazy {
|
||||
HttpClient(OkHttp) {
|
||||
install(HttpTimeout) {
|
||||
requestTimeoutMillis = 60000
|
||||
connectTimeoutMillis = 5000
|
||||
socketTimeoutMillis = 15000
|
||||
requestTimeoutMillis = 120_000
|
||||
connectTimeoutMillis = 30_000
|
||||
socketTimeoutMillis = 120_000
|
||||
}
|
||||
}
|
||||
}
|
||||
|
112
src/main/kotlin/tools/ImageEdit.kt
Normal file
112
src/main/kotlin/tools/ImageEdit.kt
Normal file
@@ -0,0 +1,112 @@
|
||||
package top.jie65535.mirai.tools
|
||||
|
||||
import com.aallam.openai.api.chat.Tool
|
||||
import com.aallam.openai.api.core.Parameters
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.header
|
||||
import io.ktor.client.request.post
|
||||
import io.ktor.client.request.setBody
|
||||
import io.ktor.client.statement.bodyAsText
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.contentType
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.add
|
||||
import kotlinx.serialization.json.addJsonObject
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import kotlinx.serialization.json.put
|
||||
import kotlinx.serialization.json.putJsonArray
|
||||
import kotlinx.serialization.json.putJsonObject
|
||||
import top.jie65535.mirai.JChatGPT
|
||||
import top.jie65535.mirai.PluginConfig
|
||||
|
||||
class ImageEdit : BaseAgent(
|
||||
tool = Tool.function(
|
||||
name = "imageEdit",
|
||||
description = "可通过调用图像编辑模型来修改图片。备注:该方法成本较高,非必要尽量不要调用。编辑图片前无需识别图片内容,图像编辑模型自己会理解图片内容!",
|
||||
parameters = Parameters.buildJsonObject {
|
||||
put("type", "object")
|
||||
putJsonObject("properties") {
|
||||
putJsonObject("image_url") {
|
||||
put("type", "string")
|
||||
put("description", "原始图片地址")
|
||||
}
|
||||
putJsonObject("prompt") {
|
||||
put("type", "string")
|
||||
put("description", "正向提示词,用来描述需要对图片进行修改的要求。")
|
||||
}
|
||||
putJsonObject("negative_prompt") {
|
||||
put("type", "string")
|
||||
put("description", "反向提示词,用来描述不希望在画面中看到的内容,可以对画面进行限制。" +
|
||||
"示例值:低分辨率、错误、最差质量、低质量、残缺、多余的手指、比例不良等。")
|
||||
}
|
||||
}
|
||||
putJsonArray("required") {
|
||||
add("image_url")
|
||||
add("prompt")
|
||||
}
|
||||
}
|
||||
)
|
||||
) {
|
||||
companion object {
|
||||
const val API_URL = "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation"
|
||||
}
|
||||
|
||||
override val isEnabled: Boolean
|
||||
get() = PluginConfig.dashScopeApiKey.isNotEmpty()
|
||||
|
||||
override val loadingMessage: String
|
||||
get() = "图片编辑中..."
|
||||
|
||||
override suspend fun execute(args: JsonObject?): String {
|
||||
requireNotNull(args)
|
||||
val imageUrl = args.getValue("image_url").jsonPrimitive.content
|
||||
val prompt = args.getValue("prompt").jsonPrimitive.content
|
||||
val negativePrompt = args["negative_prompt"]?.jsonPrimitive?.content
|
||||
val response = httpClient.post(API_URL) {
|
||||
contentType(ContentType("application", "json"))
|
||||
header("Authorization", "Bearer " + PluginConfig.dashScopeApiKey)
|
||||
setBody(buildJsonObject {
|
||||
put("model", PluginConfig.imageEditModel)
|
||||
putJsonObject("input") {
|
||||
putJsonArray("messages") {
|
||||
addJsonObject {
|
||||
put("role", "user")
|
||||
putJsonArray("content") {
|
||||
addJsonObject {
|
||||
put("image", imageUrl)
|
||||
}
|
||||
addJsonObject {
|
||||
put("text", prompt)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (negativePrompt != null) {
|
||||
putJsonObject("parameters") {
|
||||
put("negative_prompt", negativePrompt)
|
||||
}
|
||||
}
|
||||
}.toString())
|
||||
}
|
||||
|
||||
val responseJson = response.bodyAsText()
|
||||
val responseObject = Json.parseToJsonElement(responseJson).jsonObject
|
||||
return try {
|
||||
val url = responseObject
|
||||
.getValue("output").jsonObject
|
||||
.getValue("choices").jsonArray[0].jsonObject
|
||||
.getValue("message").jsonObject
|
||||
.getValue("content").jsonArray[0].jsonObject
|
||||
.getValue("image").jsonPrimitive.content
|
||||
"图片已编辑完成,发送时请务必包含完整的url和查询参数,因为下载地址存在鉴权。图片地址:$url"
|
||||
} catch (e: Exception) {
|
||||
JChatGPT.logger.error("图像编辑结果解析异常", e)
|
||||
responseObject.toString()
|
||||
}
|
||||
}
|
||||
}
|
@@ -11,7 +11,8 @@ import top.jie65535.mirai.PluginConfig
|
||||
class RunCode : BaseAgent(
|
||||
tool = Tool.function(
|
||||
name = "runCode",
|
||||
description = "执行代码,请尽量避免需要运行时输入或可能导致死循环的代码!",
|
||||
description = "运行目标代码,请尽量避免需要运行时输入或可能导致死循环的代码!" +
|
||||
"注意,这些代码对用户不可见,如果用户要求展示代码,你应该直接发送相关代码而不是执行。",
|
||||
parameters = Parameters.buildJsonObject {
|
||||
put("type", "object")
|
||||
putJsonObject("properties") {
|
||||
|
@@ -18,8 +18,8 @@ import top.jie65535.mirai.PluginConfig
|
||||
|
||||
class VisualAgent : BaseAgent(
|
||||
tool = Tool.function(
|
||||
name = "visualAgent",
|
||||
description = "可通过调用视觉模型识别图片。",
|
||||
name = "imageRecognition",
|
||||
description = "可通过调用视觉模型来识别图片内容。备注:该方法成本较高,非必要尽量不要调用。",
|
||||
parameters = Parameters.buildJsonObject {
|
||||
put("type", "object")
|
||||
putJsonObject("properties") {
|
||||
|
Reference in New Issue
Block a user