koishi-plugin-chatluna-anuneko-api-adapter 1.0.4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/anuneko-requester.d.ts +0 -5
- package/lib/index.d.ts +1 -1
- package/lib/index.js +15 -80
- package/package.json +1 -1
- package/src/anuneko-requester.ts +11 -78
- package/src/index.ts +6 -25
|
@@ -6,14 +6,9 @@ import { ChatLunaPlugin } from 'koishi-plugin-chatluna/services/chat';
|
|
|
6
6
|
import { Context } from 'koishi';
|
|
7
7
|
export declare class AnunekoRequester extends ModelRequester {
|
|
8
8
|
_pluginConfig: Config;
|
|
9
|
-
private sessionMap;
|
|
10
|
-
private modelMap;
|
|
11
9
|
constructor(ctx: Context, _configPool: ClientConfigPool<ClientConfig>, _pluginConfig: Config, _plugin: ChatLunaPlugin);
|
|
12
|
-
clearSession(userId: string): boolean;
|
|
13
|
-
clearAllSessions(): number;
|
|
14
10
|
buildHeaders(): Record<string, string>;
|
|
15
11
|
private createNewSession;
|
|
16
|
-
private switchModel;
|
|
17
12
|
private sendChoice;
|
|
18
13
|
completionStreamInternal(params: ModelRequestParams): AsyncGenerator<ChatGenerationChunk>;
|
|
19
14
|
get logger(): import("reggol");
|
package/lib/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { Context, Logger, Schema } from 'koishi';
|
|
|
2
2
|
import { ChatLunaPlugin } from 'koishi-plugin-chatluna/services/chat';
|
|
3
3
|
export declare let logger: Logger;
|
|
4
4
|
export declare let anunekoClient: any;
|
|
5
|
-
export declare const reusable =
|
|
5
|
+
export declare const reusable = false;
|
|
6
6
|
export declare const usage = "\n<p><strong>\u96F6\u6210\u672C\u3001\u5FEB\u901F\u4F53\u9A8CChatluna</strong>\u3002</p>\n<ul>\n<li><strong>API\u6765\u6E90\uFF1A</strong> anuneko.com</li>\n<li>\n<strong>\u63A5\u53E3\u5730\u5740\uFF1A</strong>\n<a href=\"https://anuneko.com\" target=\"_blank\" rel=\"noopener noreferrer\">https://anuneko.com</a>\n</li>\n</ul>\n<p><strong>\u8BF7\u6CE8\u610F\uFF1A</strong></p>\n<p>\u8BE5\u670D\u52A1\u9700\u8981\u914D\u7F6E\u6709\u6548\u7684 x-token \u624D\u80FD\u4F7F\u7528\u3002</p>\n<p>\u8BE5\u670D\u52A1\u53EF\u80FD\u9700\u8981\u79D1\u5B66\u4E0A\u7F51\u3002</p>\n<p>\u652F\u6301\u6A58\u732B(Orange Cat)\u548C\u9ED1\u732B(Exotic Shorthair)\u4E24\u79CD\u6A21\u578B\u3002</p>\n\n---\n\nx-token \u83B7\u53D6\u65B9\u6CD5\u89C1\u4ED3\u5E93\u6587\u4EF6 https://github.com/koishi-shangxue-plugins/koishi-shangxue-apps/blob/main/plugins/chatluna-anuneko-api-adapter/data/2025-12-23_19-01-43.png\n";
|
|
7
7
|
export declare function apply(ctx: Context, config: Config): void;
|
|
8
8
|
export interface Config extends ChatLunaPlugin.Config {
|
package/lib/index.js
CHANGED
|
@@ -88,24 +88,6 @@ var AnunekoRequester = class extends import_api.ModelRequester {
|
|
|
88
88
|
static {
|
|
89
89
|
__name(this, "AnunekoRequester");
|
|
90
90
|
}
|
|
91
|
-
// 存储每个用户的会话ID
|
|
92
|
-
sessionMap = /* @__PURE__ */ new Map();
|
|
93
|
-
// 存储每个用户的当前模型
|
|
94
|
-
modelMap = /* @__PURE__ */ new Map();
|
|
95
|
-
// 清理指定用户的会话
|
|
96
|
-
clearSession(userId) {
|
|
97
|
-
const hasSession = this.sessionMap.has(userId);
|
|
98
|
-
this.sessionMap.delete(userId);
|
|
99
|
-
this.modelMap.delete(userId);
|
|
100
|
-
return hasSession;
|
|
101
|
-
}
|
|
102
|
-
// 清理所有会话
|
|
103
|
-
clearAllSessions() {
|
|
104
|
-
const count = this.sessionMap.size;
|
|
105
|
-
this.sessionMap.clear();
|
|
106
|
-
this.modelMap.clear();
|
|
107
|
-
return count;
|
|
108
|
-
}
|
|
109
91
|
// 构建请求头
|
|
110
92
|
buildHeaders() {
|
|
111
93
|
const headers = {
|
|
@@ -125,7 +107,7 @@ var AnunekoRequester = class extends import_api.ModelRequester {
|
|
|
125
107
|
return headers;
|
|
126
108
|
}
|
|
127
109
|
// 创建新会话
|
|
128
|
-
async createNewSession(
|
|
110
|
+
async createNewSession(modelName) {
|
|
129
111
|
const headers = this.buildHeaders();
|
|
130
112
|
const data = { model: modelName };
|
|
131
113
|
try {
|
|
@@ -140,10 +122,7 @@ var AnunekoRequester = class extends import_api.ModelRequester {
|
|
|
140
122
|
const responseData = await response.json();
|
|
141
123
|
const chatId = responseData.chat_id || responseData.id;
|
|
142
124
|
if (chatId) {
|
|
143
|
-
this.sessionMap.set(userId, chatId);
|
|
144
|
-
this.modelMap.set(userId, modelName);
|
|
145
125
|
logInfo("New session created with ID:", chatId);
|
|
146
|
-
await this.switchModel(userId, chatId, modelName);
|
|
147
126
|
return chatId;
|
|
148
127
|
}
|
|
149
128
|
} catch (error) {
|
|
@@ -151,29 +130,6 @@ var AnunekoRequester = class extends import_api.ModelRequester {
|
|
|
151
130
|
}
|
|
152
131
|
return null;
|
|
153
132
|
}
|
|
154
|
-
// 切换模型
|
|
155
|
-
async switchModel(userId, chatId, modelName) {
|
|
156
|
-
const headers = this.buildHeaders();
|
|
157
|
-
const data = { chat_id: chatId, model: modelName };
|
|
158
|
-
try {
|
|
159
|
-
logInfo("Switching model to:", modelName);
|
|
160
|
-
const signal = AbortSignal.timeout(this._pluginConfig.requestTimeout * 1e3);
|
|
161
|
-
const response = await fetch("https://anuneko.com/api/v1/user/select_model", {
|
|
162
|
-
method: "POST",
|
|
163
|
-
headers,
|
|
164
|
-
body: JSON.stringify(data),
|
|
165
|
-
signal
|
|
166
|
-
});
|
|
167
|
-
if (response.ok) {
|
|
168
|
-
this.modelMap.set(userId, modelName);
|
|
169
|
-
logInfo("Model switched successfully");
|
|
170
|
-
return true;
|
|
171
|
-
}
|
|
172
|
-
} catch (error) {
|
|
173
|
-
this.logger.error("Failed to switch model:", error);
|
|
174
|
-
}
|
|
175
|
-
return false;
|
|
176
|
-
}
|
|
177
133
|
// 自动选择分支
|
|
178
134
|
async sendChoice(msgId) {
|
|
179
135
|
const headers = this.buildHeaders();
|
|
@@ -204,24 +160,18 @@ var AnunekoRequester = class extends import_api.ModelRequester {
|
|
|
204
160
|
return;
|
|
205
161
|
}
|
|
206
162
|
const prompt = lastMessage.content;
|
|
207
|
-
const sessionKey = lastMessage.channelId || lastMessage.id || "default";
|
|
208
|
-
logInfo("使用会话标识:", sessionKey);
|
|
209
163
|
let modelName = "Orange Cat";
|
|
210
164
|
if (params.model.includes("exotic") || params.model.includes("shorthair")) {
|
|
211
165
|
modelName = "Exotic Shorthair";
|
|
212
166
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
message: new import_messages.AIMessageChunk({ content: errorText })
|
|
222
|
-
});
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
167
|
+
const sessionId = await this.createNewSession(modelName);
|
|
168
|
+
if (!sessionId) {
|
|
169
|
+
const errorText = "创建会话失败,请稍后再试。";
|
|
170
|
+
yield new import_outputs.ChatGenerationChunk({
|
|
171
|
+
text: errorText,
|
|
172
|
+
message: new import_messages.AIMessageChunk({ content: errorText })
|
|
173
|
+
});
|
|
174
|
+
return;
|
|
225
175
|
}
|
|
226
176
|
const headers = this.buildHeaders();
|
|
227
177
|
const url = `https://anuneko.com/api/v1/msg/${sessionId}/stream`;
|
|
@@ -401,7 +351,7 @@ var AnunekoClient = class extends import_client.PlatformModelAndEmbeddingsClient
|
|
|
401
351
|
// src/index.ts
|
|
402
352
|
var logger2;
|
|
403
353
|
var anunekoClient = null;
|
|
404
|
-
var reusable =
|
|
354
|
+
var reusable = false;
|
|
405
355
|
var usage = `
|
|
406
356
|
<p><strong>零成本、快速体验Chatluna</strong>。</p>
|
|
407
357
|
<ul>
|
|
@@ -531,26 +481,11 @@ function apply(ctx, config2) {
|
|
|
531
481
|
return `❌ 请求失败: ${error.message}`;
|
|
532
482
|
}
|
|
533
483
|
});
|
|
534
|
-
ctx.command("anuneko-clean", "
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
const sessionKey = session.channelId || session.userId;
|
|
540
|
-
const requester = anunekoClient._requester;
|
|
541
|
-
if (requester && typeof requester.clearSession === "function") {
|
|
542
|
-
const cleared = requester.clearSession(sessionKey);
|
|
543
|
-
if (cleared) {
|
|
544
|
-
return "✅ 已清理当前频道的对话记录,下次对话将创建新会话";
|
|
545
|
-
} else {
|
|
546
|
-
return "✅ 当前频道还没有对话记录";
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
return "❌ 无法访问会话管理器";
|
|
550
|
-
} catch (error) {
|
|
551
|
-
logger2.error("清理失败:", error);
|
|
552
|
-
return `❌ 清理失败: ${error.message}`;
|
|
553
|
-
}
|
|
484
|
+
ctx.command("anuneko-clean", "清理当前频道的对话记录").action(async ({ session }) => {
|
|
485
|
+
return `对话历史由 ChatLuna 管理,请使用以下命令清理:
|
|
486
|
+
• chatluna room clear - 清空当前房间的对话历史
|
|
487
|
+
• chatluna room delete <房间名称> - 删除指定房间
|
|
488
|
+
• chatluna room list - 查看所有房间`;
|
|
554
489
|
});
|
|
555
490
|
ctx.on("ready", async () => {
|
|
556
491
|
if (config2.platform == null || config2.platform.length < 1) {
|
package/package.json
CHANGED
package/src/anuneko-requester.ts
CHANGED
|
@@ -19,20 +19,11 @@ import {
|
|
|
19
19
|
|
|
20
20
|
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
21
21
|
|
|
22
|
-
interface KoishiHumanMessage extends HumanMessage {
|
|
23
|
-
channelId?: string
|
|
24
|
-
}
|
|
25
|
-
|
|
26
22
|
interface InternalModelRequestParams extends ModelRequestParams {
|
|
27
23
|
input: (HumanMessage | SystemMessage)[]
|
|
28
24
|
}
|
|
29
25
|
|
|
30
26
|
export class AnunekoRequester extends ModelRequester {
|
|
31
|
-
// 存储每个用户的会话ID
|
|
32
|
-
private sessionMap = new Map<string, string>()
|
|
33
|
-
// 存储每个用户的当前模型
|
|
34
|
-
private modelMap = new Map<string, string>()
|
|
35
|
-
|
|
36
27
|
constructor(
|
|
37
28
|
ctx: Context,
|
|
38
29
|
_configPool: ClientConfigPool<ClientConfig>,
|
|
@@ -42,22 +33,6 @@ export class AnunekoRequester extends ModelRequester {
|
|
|
42
33
|
super(ctx, _configPool, _pluginConfig, _plugin)
|
|
43
34
|
}
|
|
44
35
|
|
|
45
|
-
// 清理指定用户的会话
|
|
46
|
-
public clearSession(userId: string): boolean {
|
|
47
|
-
const hasSession = this.sessionMap.has(userId)
|
|
48
|
-
this.sessionMap.delete(userId)
|
|
49
|
-
this.modelMap.delete(userId)
|
|
50
|
-
return hasSession
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// 清理所有会话
|
|
54
|
-
public clearAllSessions(): number {
|
|
55
|
-
const count = this.sessionMap.size
|
|
56
|
-
this.sessionMap.clear()
|
|
57
|
-
this.modelMap.clear()
|
|
58
|
-
return count
|
|
59
|
-
}
|
|
60
|
-
|
|
61
36
|
// 构建请求头
|
|
62
37
|
public buildHeaders() {
|
|
63
38
|
const headers: Record<string, string> = {
|
|
@@ -80,7 +55,7 @@ export class AnunekoRequester extends ModelRequester {
|
|
|
80
55
|
}
|
|
81
56
|
|
|
82
57
|
// 创建新会话
|
|
83
|
-
private async createNewSession(
|
|
58
|
+
private async createNewSession(modelName: string): Promise<string | null> {
|
|
84
59
|
const headers = this.buildHeaders()
|
|
85
60
|
const data = { model: modelName }
|
|
86
61
|
|
|
@@ -97,12 +72,7 @@ export class AnunekoRequester extends ModelRequester {
|
|
|
97
72
|
const responseData = await response.json()
|
|
98
73
|
const chatId = responseData.chat_id || responseData.id
|
|
99
74
|
if (chatId) {
|
|
100
|
-
this.sessionMap.set(userId, chatId)
|
|
101
|
-
this.modelMap.set(userId, modelName)
|
|
102
75
|
logInfo('New session created with ID:', chatId)
|
|
103
|
-
|
|
104
|
-
// 切换模型以确保一致性
|
|
105
|
-
await this.switchModel(userId, chatId, modelName)
|
|
106
76
|
return chatId
|
|
107
77
|
}
|
|
108
78
|
} catch (error) {
|
|
@@ -112,33 +82,6 @@ export class AnunekoRequester extends ModelRequester {
|
|
|
112
82
|
return null
|
|
113
83
|
}
|
|
114
84
|
|
|
115
|
-
// 切换模型
|
|
116
|
-
private async switchModel(userId: string, chatId: string, modelName: string): Promise<boolean> {
|
|
117
|
-
const headers = this.buildHeaders()
|
|
118
|
-
const data = { chat_id: chatId, model: modelName }
|
|
119
|
-
|
|
120
|
-
try {
|
|
121
|
-
logInfo('Switching model to:', modelName)
|
|
122
|
-
const signal = AbortSignal.timeout(this._pluginConfig.requestTimeout * 1000)
|
|
123
|
-
const response = await fetch('https://anuneko.com/api/v1/user/select_model', {
|
|
124
|
-
method: 'POST',
|
|
125
|
-
headers,
|
|
126
|
-
body: JSON.stringify(data),
|
|
127
|
-
signal
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
if (response.ok) {
|
|
131
|
-
this.modelMap.set(userId, modelName)
|
|
132
|
-
logInfo('Model switched successfully')
|
|
133
|
-
return true
|
|
134
|
-
}
|
|
135
|
-
} catch (error) {
|
|
136
|
-
this.logger.error('Failed to switch model:', error)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return false
|
|
140
|
-
}
|
|
141
|
-
|
|
142
85
|
// 自动选择分支
|
|
143
86
|
private async sendChoice(msgId: string): Promise<void> {
|
|
144
87
|
const headers = this.buildHeaders()
|
|
@@ -166,7 +109,7 @@ export class AnunekoRequester extends ModelRequester {
|
|
|
166
109
|
// 过滤掉所有非 HumanMessage 的消息,并只取最后一条
|
|
167
110
|
const humanMessages = internalParams.input.filter(
|
|
168
111
|
(message) => message instanceof HumanMessage
|
|
169
|
-
)
|
|
112
|
+
)
|
|
170
113
|
const lastMessage = humanMessages.at(-1)
|
|
171
114
|
|
|
172
115
|
logInfo('Receive params from chatluna', JSON.stringify(params, null, 2))
|
|
@@ -177,10 +120,6 @@ export class AnunekoRequester extends ModelRequester {
|
|
|
177
120
|
}
|
|
178
121
|
|
|
179
122
|
const prompt = lastMessage.content as string
|
|
180
|
-
// 使用 channelId 作为会话标识,如果没有则使用 userId
|
|
181
|
-
const sessionKey = lastMessage.channelId || lastMessage.id || 'default'
|
|
182
|
-
|
|
183
|
-
logInfo('使用会话标识:', sessionKey)
|
|
184
123
|
|
|
185
124
|
// 从模型名称推断使用的模型
|
|
186
125
|
let modelName = 'Orange Cat' // 默认橘猫
|
|
@@ -188,21 +127,15 @@ export class AnunekoRequester extends ModelRequester {
|
|
|
188
127
|
modelName = 'Exotic Shorthair'
|
|
189
128
|
}
|
|
190
129
|
|
|
191
|
-
//
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
yield new ChatGenerationChunk({
|
|
201
|
-
text: errorText,
|
|
202
|
-
message: new AIMessageChunk({ content: errorText })
|
|
203
|
-
})
|
|
204
|
-
return
|
|
205
|
-
}
|
|
130
|
+
// 每次请求都创建新会话
|
|
131
|
+
const sessionId = await this.createNewSession(modelName)
|
|
132
|
+
if (!sessionId) {
|
|
133
|
+
const errorText = '创建会话失败,请稍后再试。'
|
|
134
|
+
yield new ChatGenerationChunk({
|
|
135
|
+
text: errorText,
|
|
136
|
+
message: new AIMessageChunk({ content: errorText })
|
|
137
|
+
})
|
|
138
|
+
return
|
|
206
139
|
}
|
|
207
140
|
|
|
208
141
|
const headers = this.buildHeaders()
|
package/src/index.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { initializeLogger, logInfo } from './logger'
|
|
|
10
10
|
|
|
11
11
|
export let logger: Logger
|
|
12
12
|
export let anunekoClient: any = null
|
|
13
|
-
export const reusable =
|
|
13
|
+
export const reusable = false
|
|
14
14
|
export const usage = `
|
|
15
15
|
<p><strong>零成本、快速体验Chatluna</strong>。</p>
|
|
16
16
|
<ul>
|
|
@@ -176,31 +176,12 @@ export function apply(ctx: Context, config: Config) {
|
|
|
176
176
|
})
|
|
177
177
|
|
|
178
178
|
// 清理命令
|
|
179
|
-
ctx.command('anuneko-clean', '
|
|
179
|
+
ctx.command('anuneko-clean', '清理当前频道的对话记录')
|
|
180
180
|
.action(async ({ session }) => {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
// 使用 channelId 作为会话标识
|
|
187
|
-
const sessionKey = session.channelId || session.userId
|
|
188
|
-
const requester = anunekoClient._requester
|
|
189
|
-
|
|
190
|
-
if (requester && typeof requester.clearSession === 'function') {
|
|
191
|
-
const cleared = requester.clearSession(sessionKey)
|
|
192
|
-
if (cleared) {
|
|
193
|
-
return '✅ 已清理当前频道的对话记录,下次对话将创建新会话'
|
|
194
|
-
} else {
|
|
195
|
-
return '✅ 当前频道还没有对话记录'
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return '❌ 无法访问会话管理器'
|
|
200
|
-
} catch (error) {
|
|
201
|
-
logger.error('清理失败:', error)
|
|
202
|
-
return `❌ 清理失败: ${error.message}`
|
|
203
|
-
}
|
|
181
|
+
return `对话历史由 ChatLuna 管理,请使用以下命令清理:
|
|
182
|
+
• chatluna room clear - 清空当前房间的对话历史
|
|
183
|
+
• chatluna room delete <房间名称> - 删除指定房间
|
|
184
|
+
• chatluna room list - 查看所有房间`
|
|
204
185
|
})
|
|
205
186
|
|
|
206
187
|
ctx.on('ready', async () => {
|