koishi-plugin-chatluna-anuneko-api-adapter 1.0.3 → 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.
@@ -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,8 +2,8 @@ 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 = true;
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";
5
+ export declare const reusable = false;
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 {
9
9
  platform: string;
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(userId, modelName) {
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
- let sessionId = this.sessionMap.get(sessionKey);
214
- const currentModel = this.modelMap.get(sessionKey);
215
- if (!sessionId || currentModel !== modelName) {
216
- sessionId = await this.createNewSession(sessionKey, modelName);
217
- if (!sessionId) {
218
- const errorText = "创建会话失败,请稍后再试。";
219
- yield new import_outputs.ChatGenerationChunk({
220
- text: errorText,
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 = true;
354
+ var reusable = false;
405
355
  var usage = `
406
356
  <p><strong>零成本、快速体验Chatluna</strong>。</p>
407
357
  <ul>
@@ -415,6 +365,10 @@ var usage = `
415
365
  <p>该服务需要配置有效的 x-token 才能使用。</p>
416
366
  <p>该服务可能需要科学上网。</p>
417
367
  <p>支持橘猫(Orange Cat)和黑猫(Exotic Shorthair)两种模型。</p>
368
+
369
+ ---
370
+
371
+ x-token 获取方法见仓库文件 https://github.com/koishi-shangxue-plugins/koishi-shangxue-apps/blob/main/plugins/chatluna-anuneko-api-adapter/data/2025-12-23_19-01-43.png
418
372
  `;
419
373
  function apply(ctx, config2) {
420
374
  logger2 = (0, import_logger3.createLogger)(ctx, "chatluna-anuneko-api-adapter");
@@ -527,26 +481,11 @@ function apply(ctx, config2) {
527
481
  return `❌ 请求失败: ${error.message}`;
528
482
  }
529
483
  });
530
- ctx.command("anuneko-clean", "清理当前频道的 anuneko 对话记录").action(async ({ session }) => {
531
- try {
532
- if (!anunekoClient) {
533
- return "❌ anuneko 客户端未初始化";
534
- }
535
- const sessionKey = session.channelId || session.userId;
536
- const requester = anunekoClient._requester;
537
- if (requester && typeof requester.clearSession === "function") {
538
- const cleared = requester.clearSession(sessionKey);
539
- if (cleared) {
540
- return "✅ 已清理当前频道的对话记录,下次对话将创建新会话";
541
- } else {
542
- return "✅ 当前频道还没有对话记录";
543
- }
544
- }
545
- return "❌ 无法访问会话管理器";
546
- } catch (error) {
547
- logger2.error("清理失败:", error);
548
- return `❌ 清理失败: ${error.message}`;
549
- }
484
+ ctx.command("anuneko-clean", "清理当前频道的对话记录").action(async ({ session }) => {
485
+ return `对话历史由 ChatLuna 管理,请使用以下命令清理:
486
+ chatluna room clear - 清空当前房间的对话历史
487
+ chatluna room delete <房间名称> - 删除指定房间
488
+ • chatluna room list - 查看所有房间`;
550
489
  });
551
490
  ctx.on("ready", async () => {
552
491
  if (config2.platform == null || config2.platform.length < 1) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-chatluna-anuneko-api-adapter",
3
3
  "description": "anuneko API adapter for ChatLuna, using pearktrue API.",
4
- "version": "1.0.3",
4
+ "version": "1.1.0",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
package/readme.md CHANGED
@@ -3,3 +3,5 @@
3
3
  [![npm](https://img.shields.io/npm/v/koishi-plugin-chatluna-anuneko-api-adapter?style=flat-square)](https://www.npmjs.com/package/koishi-plugin-chatluna-anuneko-api-adapter)
4
4
 
5
5
  anuneko api adapter for chatluna
6
+
7
+ `x-token` 获取方法见仓库文件 https://github.com/koishi-shangxue-plugins/koishi-shangxue-apps/blob/main/plugins/chatluna-anuneko-api-adapter/data/2025-12-23_19-01-43.png
@@ -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(userId: string, modelName: string): Promise<string | null> {
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
- ) as KoishiHumanMessage[]
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
- let sessionId = this.sessionMap.get(sessionKey)
193
- const currentModel = this.modelMap.get(sessionKey)
194
-
195
- // 如果没有会话或模型不匹配,创建新会话
196
- if (!sessionId || currentModel !== modelName) {
197
- sessionId = await this.createNewSession(sessionKey, modelName)
198
- if (!sessionId) {
199
- const errorText = '创建会话失败,请稍后再试。'
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 = true
13
+ export const reusable = false
14
14
  export const usage = `
15
15
  <p><strong>零成本、快速体验Chatluna</strong>。</p>
16
16
  <ul>
@@ -24,6 +24,10 @@ export const usage = `
24
24
  <p>该服务需要配置有效的 x-token 才能使用。</p>
25
25
  <p>该服务可能需要科学上网。</p>
26
26
  <p>支持橘猫(Orange Cat)和黑猫(Exotic Shorthair)两种模型。</p>
27
+
28
+ ---
29
+
30
+ x-token 获取方法见仓库文件 https://github.com/koishi-shangxue-plugins/koishi-shangxue-apps/blob/main/plugins/chatluna-anuneko-api-adapter/data/2025-12-23_19-01-43.png
27
31
  `
28
32
 
29
33
  export function apply(ctx: Context, config: Config) {
@@ -172,31 +176,12 @@ export function apply(ctx: Context, config: Config) {
172
176
  })
173
177
 
174
178
  // 清理命令
175
- ctx.command('anuneko-clean', '清理当前频道的 anuneko 对话记录')
179
+ ctx.command('anuneko-clean', '清理当前频道的对话记录')
176
180
  .action(async ({ session }) => {
177
- try {
178
- if (!anunekoClient) {
179
- return '❌ anuneko 客户端未初始化'
180
- }
181
-
182
- // 使用 channelId 作为会话标识
183
- const sessionKey = session.channelId || session.userId
184
- const requester = anunekoClient._requester
185
-
186
- if (requester && typeof requester.clearSession === 'function') {
187
- const cleared = requester.clearSession(sessionKey)
188
- if (cleared) {
189
- return '✅ 已清理当前频道的对话记录,下次对话将创建新会话'
190
- } else {
191
- return '✅ 当前频道还没有对话记录'
192
- }
193
- }
194
-
195
- return '❌ 无法访问会话管理器'
196
- } catch (error) {
197
- logger.error('清理失败:', error)
198
- return `❌ 清理失败: ${error.message}`
199
- }
181
+ return `对话历史由 ChatLuna 管理,请使用以下命令清理:
182
+ chatluna room clear - 清空当前房间的对话历史
183
+ chatluna room delete <房间名称> - 删除指定房间
184
+ • chatluna room list - 查看所有房间`
200
185
  })
201
186
 
202
187
  ctx.on('ready', async () => {