@ynhcj/xiaoyi-channel 0.0.75-beta → 0.0.75-next
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/dist/index.d.ts +6 -9
- package/dist/index.js +29 -23
- package/dist/src/bot.js +27 -3
- package/dist/src/channel.js +11 -23
- package/dist/src/cspl/call-api.js +14 -11
- package/dist/src/cspl/config.js +3 -3
- package/dist/src/cspl/constants.d.ts +2 -0
- package/dist/src/cspl/constants.js +12 -0
- package/dist/src/cspl/utils.js +4 -2
- package/dist/src/file-download.js +3 -6
- package/dist/src/file-upload.js +52 -5
- package/dist/src/login-token-handler.d.ts +8 -0
- package/dist/src/login-token-handler.js +60 -0
- package/dist/src/message-queue.d.ts +17 -0
- package/dist/src/message-queue.js +51 -0
- package/dist/src/monitor.js +54 -3
- package/dist/src/outbound.js +2 -7
- package/dist/src/provider.d.ts +2 -1
- package/dist/src/provider.js +486 -33
- package/dist/src/reply-dispatcher.js +6 -0
- package/dist/src/runtime.d.ts +3 -11
- package/dist/src/runtime.js +6 -18
- package/dist/src/self-evolution-handler.d.ts +7 -0
- package/dist/src/self-evolution-handler.js +140 -0
- package/dist/src/self-evolution-keyword.d.ts +9 -0
- package/dist/src/self-evolution-keyword.js +147 -0
- package/dist/src/self-evolution-tool-result-nudge.d.ts +3 -0
- package/dist/src/self-evolution-tool-result-nudge.js +96 -0
- package/dist/src/skill-retriever/config.d.ts +4 -0
- package/dist/src/skill-retriever/config.js +23 -0
- package/dist/src/skill-retriever/hooks.d.ts +22 -0
- package/dist/src/skill-retriever/hooks.js +82 -0
- package/dist/src/skill-retriever/tool-search.d.ts +16 -0
- package/dist/src/skill-retriever/tool-search.js +172 -0
- package/dist/src/skill-retriever/types.d.ts +36 -0
- package/dist/src/skill-retriever/types.js +1 -0
- package/dist/src/task-manager.d.ts +4 -0
- package/dist/src/task-manager.js +6 -0
- package/dist/src/tools/call-device-tool.d.ts +5 -0
- package/dist/src/tools/call-device-tool.js +130 -0
- package/dist/src/tools/create-alarm-tool.js +5 -16
- package/dist/src/tools/delete-alarm-tool.js +1 -4
- package/dist/src/tools/device-tool-map.js +5 -4
- package/dist/src/tools/find-pc-devices-tool.d.ts +5 -0
- package/dist/src/tools/find-pc-devices-tool.js +98 -0
- package/dist/src/tools/get-alarm-tool-schema.d.ts +16 -0
- package/dist/src/tools/get-alarm-tool-schema.js +11 -0
- package/dist/src/tools/get-calendar-tool-schema.d.ts +16 -0
- package/dist/src/tools/get-calendar-tool-schema.js +9 -0
- package/dist/src/tools/get-collection-tool-schema.d.ts +16 -0
- package/dist/src/tools/get-collection-tool-schema.js +10 -0
- package/dist/src/tools/get-contact-tool-schema.d.ts +16 -0
- package/dist/src/tools/get-contact-tool-schema.js +11 -0
- package/dist/src/tools/get-device-file-tool-schema.d.ts +16 -0
- package/dist/src/tools/get-device-file-tool-schema.js +10 -0
- package/dist/src/tools/get-email-tool-schema.d.ts +16 -0
- package/dist/src/tools/get-email-tool-schema.js +9 -0
- package/dist/src/tools/get-note-tool-schema.d.ts +16 -0
- package/dist/src/tools/get-note-tool-schema.js +10 -0
- package/dist/src/tools/get-photo-tool-schema.d.ts +16 -0
- package/dist/src/tools/get-photo-tool-schema.js +10 -0
- package/dist/src/tools/image-reading-tool.js +4 -7
- package/dist/src/tools/login-token-tool.d.ts +5 -0
- package/dist/src/tools/login-token-tool.js +136 -0
- package/dist/src/tools/modify-alarm-tool.js +10 -23
- package/dist/src/tools/query-app-message-tool.d.ts +4 -0
- package/dist/src/tools/query-app-message-tool.js +138 -0
- package/dist/src/tools/query-memory-data-tool.d.ts +4 -0
- package/dist/src/tools/query-memory-data-tool.js +154 -0
- package/dist/src/tools/query-todo-task-tool.d.ts +4 -0
- package/dist/src/tools/query-todo-task-tool.js +133 -0
- package/dist/src/tools/save-file-to-phone-tool.d.ts +5 -0
- package/dist/src/tools/save-file-to-phone-tool.js +166 -0
- package/dist/src/tools/save-media-to-gallery-tool.js +3 -7
- package/dist/src/tools/save-self-evolution-skill-tool.d.ts +1 -0
- package/dist/src/tools/save-self-evolution-skill-tool.js +412 -0
- package/dist/src/tools/schema-tool-factory.d.ts +27 -0
- package/dist/src/tools/schema-tool-factory.js +32 -0
- package/dist/src/tools/search-alarm-tool.js +6 -13
- package/dist/src/tools/search-calendar-tool.js +2 -0
- package/dist/src/tools/search-email-tool.d.ts +5 -0
- package/dist/src/tools/search-email-tool.js +137 -0
- package/dist/src/tools/search-file-tool.js +4 -4
- package/dist/src/tools/search-message-tool.js +1 -0
- package/dist/src/tools/search-photo-gallery-tool.js +2 -2
- package/dist/src/tools/send-email-tool.d.ts +4 -0
- package/dist/src/tools/send-email-tool.js +134 -0
- package/dist/src/tools/send-file-to-user-tool.js +3 -5
- package/dist/src/tools/session-manager.js +2 -0
- package/dist/src/tools/upload-file-tool.js +4 -4
- package/dist/src/tools/upload-photo-tool.js +2 -2
- package/dist/src/tools/xiaoyi-add-collection-tool.js +23 -4
- package/dist/src/tools/xiaoyi-collection-tool.js +2 -1
- package/dist/src/tools/xiaoyi-delete-collection-tool.js +1 -1
- package/dist/src/utils/runtime-manager.js +24 -2
- package/dist/src/utils/self-evolution-manager.d.ts +10 -0
- package/dist/src/utils/self-evolution-manager.js +68 -0
- package/dist/src/utils/tool-call-nudge-manager.d.ts +16 -0
- package/dist/src/utils/tool-call-nudge-manager.js +47 -0
- package/dist/src/websocket.d.ts +3 -0
- package/dist/src/websocket.js +69 -0
- package/openclaw.plugin.json +21 -0
- package/package.json +3 -3
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import * as os from "os";
|
|
4
|
+
const SKILL_ID = "celia_find_skills";
|
|
5
|
+
const PLUGIN_LOG_PREFIX = "[skill-retriever]";
|
|
6
|
+
export function extractUserQuery(fullPrompt) {
|
|
7
|
+
const lastNewlineIndex = fullPrompt.lastIndexOf("\n");
|
|
8
|
+
if (lastNewlineIndex === -1) {
|
|
9
|
+
return fullPrompt.trim();
|
|
10
|
+
}
|
|
11
|
+
const afterLastNewline = fullPrompt.slice(lastNewlineIndex + 1).trim();
|
|
12
|
+
if (!afterLastNewline || afterLastNewline === "```") {
|
|
13
|
+
return "";
|
|
14
|
+
}
|
|
15
|
+
if (fullPrompt.toLowerCase().includes("cron")) {
|
|
16
|
+
return "";
|
|
17
|
+
}
|
|
18
|
+
return afterLastNewline;
|
|
19
|
+
}
|
|
20
|
+
function expandPath(filePath) {
|
|
21
|
+
if (filePath.startsWith("~")) {
|
|
22
|
+
return path.join(os.homedir(), filePath.slice(1).replace(/^\/+/, ""));
|
|
23
|
+
}
|
|
24
|
+
return filePath;
|
|
25
|
+
}
|
|
26
|
+
export function readEnvFile(filePath) {
|
|
27
|
+
const expandedPath = expandPath(filePath);
|
|
28
|
+
const envDict = {};
|
|
29
|
+
try {
|
|
30
|
+
const content = fs.readFileSync(expandedPath, "utf-8");
|
|
31
|
+
for (const line of content.split("\n")) {
|
|
32
|
+
const trimmed = line.trim();
|
|
33
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const eqIndex = trimmed.indexOf("=");
|
|
37
|
+
if (eqIndex > 0) {
|
|
38
|
+
let key = trimmed.substring(0, eqIndex).trim();
|
|
39
|
+
const value = trimmed.substring(eqIndex + 1).trim();
|
|
40
|
+
key = key.replace(/-/g, "_");
|
|
41
|
+
envDict[key] = value;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// File not found or read error - return empty config
|
|
47
|
+
}
|
|
48
|
+
return envDict;
|
|
49
|
+
}
|
|
50
|
+
export function getInstalledSkills() {
|
|
51
|
+
const skillsDir = expandPath("~/.openclaw/workspace/skills");
|
|
52
|
+
const installedSkills = [];
|
|
53
|
+
try {
|
|
54
|
+
if (fs.existsSync(skillsDir) && fs.statSync(skillsDir).isDirectory()) {
|
|
55
|
+
const entries = fs.readdirSync(skillsDir);
|
|
56
|
+
for (const entry of entries) {
|
|
57
|
+
const entryPath = path.join(skillsDir, entry);
|
|
58
|
+
if (fs.statSync(entryPath).isDirectory()) {
|
|
59
|
+
installedSkills.push(entry);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Directory doesn't exist or read error - return empty list
|
|
66
|
+
}
|
|
67
|
+
return installedSkills;
|
|
68
|
+
}
|
|
69
|
+
function formatSkillData(rawSkills, installedSkills) {
|
|
70
|
+
const formattedSkills = [];
|
|
71
|
+
for (const skill of rawSkills) {
|
|
72
|
+
const isInstalled = installedSkills.includes(skill.skillId);
|
|
73
|
+
formattedSkills.push({
|
|
74
|
+
skillId: skill.skillId,
|
|
75
|
+
skillName: skill.skillName,
|
|
76
|
+
skillDesc: skill.skillDesc,
|
|
77
|
+
downloadPath: skill.packUrl,
|
|
78
|
+
status: isInstalled ? "已安装" : "未安装",
|
|
79
|
+
rrfScore: skill.rrfScore,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return formattedSkills;
|
|
83
|
+
}
|
|
84
|
+
export async function searchTools(options) {
|
|
85
|
+
const { query, maxTools = 5, includeUninstalledOnly = true, envFilePath = "~/.openclaw/.xiaoyienv", serviceUrl: configServiceUrl, apiKey: configApiKey, uid: configUid, timeoutMs = 1000, } = options;
|
|
86
|
+
const envConfig = readEnvFile(envFilePath);
|
|
87
|
+
const hasRequiredConfig = !!envConfig.SERVICE_URL && !!envConfig.PERSONAL_API_KEY && !!envConfig.PERSONAL_UID;
|
|
88
|
+
const serviceUrl = configServiceUrl ?? envConfig.SERVICE_URL;
|
|
89
|
+
const apiKey = configApiKey ?? envConfig.PERSONAL_API_KEY;
|
|
90
|
+
const uid = configUid ?? envConfig.PERSONAL_UID;
|
|
91
|
+
if (!serviceUrl || !apiKey || !uid) {
|
|
92
|
+
console.warn(`${PLUGIN_LOG_PREFIX} Missing required configuration. serviceUrl: "${serviceUrl}", apiKey: "${apiKey ? '(set)' : '(missing)'} ", uid: "${uid ? '(set)' : '(missing)'}"`);
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const traceId = crypto.randomUUID();
|
|
96
|
+
const apiUrl = `${serviceUrl}/celia-claw/v1/rest-api/skill/execute`;
|
|
97
|
+
const headers = {
|
|
98
|
+
"Content-Type": "application/json",
|
|
99
|
+
"x-skill-id": SKILL_ID,
|
|
100
|
+
"x-hag-trace-id": traceId,
|
|
101
|
+
"x-uid": uid,
|
|
102
|
+
"x-api-key": apiKey,
|
|
103
|
+
"x-request-from": "openclaw",
|
|
104
|
+
};
|
|
105
|
+
const payload = { query };
|
|
106
|
+
try {
|
|
107
|
+
const response = await fetch(apiUrl, {
|
|
108
|
+
method: "POST",
|
|
109
|
+
headers,
|
|
110
|
+
body: JSON.stringify(payload),
|
|
111
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
112
|
+
});
|
|
113
|
+
if (!response.ok) {
|
|
114
|
+
console.warn(`${PLUGIN_LOG_PREFIX} HTTP error: ${response.status} ${response.statusText}`);
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
console.log(`${PLUGIN_LOG_PREFIX} Received response, status: ${response.status}`);
|
|
118
|
+
const responseData = await response.json();
|
|
119
|
+
if (responseData.errorCode === "0" &&
|
|
120
|
+
responseData.content &&
|
|
121
|
+
responseData.content.skills) {
|
|
122
|
+
const rawSkills = responseData.content.skills;
|
|
123
|
+
const installedSkills = getInstalledSkills();
|
|
124
|
+
const formattedData = formatSkillData(rawSkills, installedSkills);
|
|
125
|
+
const topTools = formattedData.slice(0, 2);
|
|
126
|
+
const allInstalled = topTools.every((tool) => tool.status === "已安装");
|
|
127
|
+
if (allInstalled) {
|
|
128
|
+
console.log(`${PLUGIN_LOG_PREFIX} [DEBUG] All top 2 skills are installed, returning null`);
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
const hasInstalledWithHighScore = topTools.some((tool) => tool.status === "已安装" && (tool.rrfScore ?? 0) >= 0.016);
|
|
132
|
+
if (hasInstalledWithHighScore) {
|
|
133
|
+
console.log(`${PLUGIN_LOG_PREFIX} [DEBUG] Top 2 has installed skill with rrfScore >= 0.016, returning null`);
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
let filteredTools = topTools.filter((tool) => tool.status === "未安装" && (tool.rrfScore ?? 0) >= 0.016);
|
|
137
|
+
console.log(`${PLUGIN_LOG_PREFIX} [DEBUG] After filtering uninstalled with rrfScore >= 0.016: ${filteredTools.length}, details: ${filteredTools.map((t) => `${t.skillId}(rrfScore=${t.rrfScore})`).join(", ")}`);
|
|
138
|
+
if (filteredTools.length === 0) {
|
|
139
|
+
console.log(`${PLUGIN_LOG_PREFIX} [DEBUG] No uninstalled skills with rrfScore >= 0.016, returning null`);
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
tools: filteredTools,
|
|
144
|
+
query,
|
|
145
|
+
timestamp: Date.now(),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
console.warn(`${PLUGIN_LOG_PREFIX} Invalid response format: ${JSON.stringify(responseData).slice(0, 200)}`);
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
const errorName = error instanceof Error ? error.name : "Unknown";
|
|
153
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
154
|
+
const errorCause = error instanceof Error && error.cause ? JSON.stringify(error.cause) : "N/A";
|
|
155
|
+
const errorStack = error instanceof Error ? error.stack?.split("\n").slice(0, 3).join(" | ") : "N/A";
|
|
156
|
+
console.warn(`${PLUGIN_LOG_PREFIX} [ERROR] Fetch failed - name: ${errorName}, message: ${errorMessage}, cause: ${errorCause}, stack: ${errorStack}`);
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
export function formatToolsForContext(result, includeInstallUrl = true) {
|
|
161
|
+
if (!result.tools || result.tools.length === 0) {
|
|
162
|
+
return "";
|
|
163
|
+
}
|
|
164
|
+
const toolDescriptions = [];
|
|
165
|
+
for (const tool of result.tools) {
|
|
166
|
+
let description = `### ${tool.skillName}\n`;
|
|
167
|
+
description += `name: ${tool.skillId}\n`;
|
|
168
|
+
description += `description: ${tool.skillDesc}\n`;
|
|
169
|
+
toolDescriptions.push(description);
|
|
170
|
+
}
|
|
171
|
+
return toolDescriptions.join("\n\n");
|
|
172
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface ToolRetrieverConfig {
|
|
2
|
+
enabled: boolean;
|
|
3
|
+
maxTools: number;
|
|
4
|
+
includeUninstalledOnly: boolean;
|
|
5
|
+
envFilePath: string;
|
|
6
|
+
serviceUrl?: string;
|
|
7
|
+
apiKey?: string;
|
|
8
|
+
uid?: string;
|
|
9
|
+
timeoutMs?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface RawSkill {
|
|
12
|
+
skillId: string;
|
|
13
|
+
skillName: string;
|
|
14
|
+
skillDesc: string;
|
|
15
|
+
packUrl: string;
|
|
16
|
+
rrfScore?: number;
|
|
17
|
+
}
|
|
18
|
+
export interface FormattedSkill {
|
|
19
|
+
skillId: string;
|
|
20
|
+
skillName: string;
|
|
21
|
+
skillDesc: string;
|
|
22
|
+
downloadPath: string;
|
|
23
|
+
status: "已安装" | "未安装";
|
|
24
|
+
rrfScore?: number;
|
|
25
|
+
}
|
|
26
|
+
export interface ToolSearchResult {
|
|
27
|
+
tools: FormattedSkill[];
|
|
28
|
+
query: string;
|
|
29
|
+
timestamp: number;
|
|
30
|
+
}
|
|
31
|
+
export interface EnvConfig {
|
|
32
|
+
PERSONAL_API_KEY?: string;
|
|
33
|
+
PERSONAL_UID?: string;
|
|
34
|
+
SERVICE_URL?: string;
|
|
35
|
+
[key: string]: string | undefined;
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -48,6 +48,10 @@ export declare function hasActiveTask(sessionId: string): boolean;
|
|
|
48
48
|
* 获取完整的binding信息(用于调试)
|
|
49
49
|
*/
|
|
50
50
|
export declare function getTaskIdBinding(sessionId: string): TaskIdBinding | null;
|
|
51
|
+
/**
|
|
52
|
+
* 获取所有活跃的 task bindings(用于 gateway_stop 通知等场景)
|
|
53
|
+
*/
|
|
54
|
+
export declare function getAllActiveTaskBindings(): TaskIdBinding[];
|
|
51
55
|
/**
|
|
52
56
|
* 强制清理(错误恢复用)
|
|
53
57
|
*/
|
package/dist/src/task-manager.js
CHANGED
|
@@ -127,6 +127,12 @@ export function hasActiveTask(sessionId) {
|
|
|
127
127
|
export function getTaskIdBinding(sessionId) {
|
|
128
128
|
return activeTaskIds.get(sessionId) ?? null;
|
|
129
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* 获取所有活跃的 task bindings(用于 gateway_stop 通知等场景)
|
|
132
|
+
*/
|
|
133
|
+
export function getAllActiveTaskBindings() {
|
|
134
|
+
return Array.from(activeTaskIds.values());
|
|
135
|
+
}
|
|
130
136
|
/**
|
|
131
137
|
* 强制清理(错误恢复用)
|
|
132
138
|
*/
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { noteTool } from "./note-tool.js";
|
|
2
|
+
import { searchNoteTool } from "./search-note-tool.js";
|
|
3
|
+
import { modifyNoteTool } from "./modify-note-tool.js";
|
|
4
|
+
import { createAlarmTool } from "./create-alarm-tool.js";
|
|
5
|
+
import { searchAlarmTool } from "./search-alarm-tool.js";
|
|
6
|
+
import { modifyAlarmTool } from "./modify-alarm-tool.js";
|
|
7
|
+
import { deleteAlarmTool } from "./delete-alarm-tool.js";
|
|
8
|
+
import { searchContactTool } from "./search-contact-tool.js";
|
|
9
|
+
import { callPhoneTool } from "./call-phone-tool.js";
|
|
10
|
+
import { searchMessageTool } from "./search-message-tool.js";
|
|
11
|
+
import { sendMessageTool } from "./send-message-tool.js";
|
|
12
|
+
import { xiaoyiAddCollectionTool } from "./xiaoyi-add-collection-tool.js";
|
|
13
|
+
import { xiaoyiCollectionTool } from "./xiaoyi-collection-tool.js";
|
|
14
|
+
import { xiaoyiDeleteCollectionTool } from "./xiaoyi-delete-collection-tool.js";
|
|
15
|
+
import { calendarTool } from "./calendar-tool.js";
|
|
16
|
+
import { searchCalendarTool } from "./search-calendar-tool.js";
|
|
17
|
+
import { searchPhotoGalleryTool } from "./search-photo-gallery-tool.js";
|
|
18
|
+
import { uploadPhotoTool } from "./upload-photo-tool.js";
|
|
19
|
+
import { saveMediaToGalleryTool } from "./save-media-to-gallery-tool.js";
|
|
20
|
+
import { searchFileTool } from "./search-file-tool.js";
|
|
21
|
+
import { uploadFileTool } from "./upload-file-tool.js";
|
|
22
|
+
import { saveFileToPhoneTool } from "./save-file-to-phone-tool.js";
|
|
23
|
+
import { sendEmailTool } from "./send-email-tool.js";
|
|
24
|
+
import { searchEmailTool } from "./search-email-tool.js";
|
|
25
|
+
import { sendStatusUpdate } from "../formatter.js";
|
|
26
|
+
import { getCurrentSessionContext } from "./session-manager.js";
|
|
27
|
+
import { getCurrentTaskId, getCurrentMessageId } from "../task-manager.js";
|
|
28
|
+
/**
|
|
29
|
+
* 端工具注册表 —— 按 name 索引所有可通过 call_device_tool 调度的工具。
|
|
30
|
+
*/
|
|
31
|
+
const deviceToolRegistry = new Map([
|
|
32
|
+
[noteTool.name, noteTool],
|
|
33
|
+
[searchNoteTool.name, searchNoteTool],
|
|
34
|
+
[modifyNoteTool.name, modifyNoteTool],
|
|
35
|
+
[createAlarmTool.name, createAlarmTool],
|
|
36
|
+
[searchAlarmTool.name, searchAlarmTool],
|
|
37
|
+
[modifyAlarmTool.name, modifyAlarmTool],
|
|
38
|
+
[deleteAlarmTool.name, deleteAlarmTool],
|
|
39
|
+
[searchContactTool.name, searchContactTool],
|
|
40
|
+
[callPhoneTool.name, callPhoneTool],
|
|
41
|
+
[searchMessageTool.name, searchMessageTool],
|
|
42
|
+
[sendMessageTool.name, sendMessageTool],
|
|
43
|
+
[xiaoyiAddCollectionTool.name, xiaoyiAddCollectionTool],
|
|
44
|
+
[xiaoyiCollectionTool.name, xiaoyiCollectionTool],
|
|
45
|
+
[xiaoyiDeleteCollectionTool.name, xiaoyiDeleteCollectionTool],
|
|
46
|
+
[calendarTool.name, calendarTool],
|
|
47
|
+
[searchCalendarTool.name, searchCalendarTool],
|
|
48
|
+
[searchPhotoGalleryTool.name, searchPhotoGalleryTool],
|
|
49
|
+
[uploadPhotoTool.name, uploadPhotoTool],
|
|
50
|
+
[saveMediaToGalleryTool.name, saveMediaToGalleryTool],
|
|
51
|
+
[searchFileTool.name, searchFileTool],
|
|
52
|
+
[uploadFileTool.name, uploadFileTool],
|
|
53
|
+
[saveFileToPhoneTool.name, saveFileToPhoneTool],
|
|
54
|
+
[sendEmailTool.name, sendEmailTool],
|
|
55
|
+
[searchEmailTool.name, searchEmailTool],
|
|
56
|
+
]);
|
|
57
|
+
/**
|
|
58
|
+
* call_device_tool - 通用端工具调度器。
|
|
59
|
+
* LLM 必须先通过 get_xxx_tool_schema 获取具体工具 schema,再用本工具执行。
|
|
60
|
+
*/
|
|
61
|
+
export const callDeviceTool = {
|
|
62
|
+
name: "call_device_tool",
|
|
63
|
+
label: "Call Device Tool",
|
|
64
|
+
description: "用户设备侧工具调用。必须先调用get_xxx_tool_schema获取了具体的工具schema,才能使用本工具执行对应设备侧工具。",
|
|
65
|
+
parameters: {
|
|
66
|
+
type: "object",
|
|
67
|
+
properties: {
|
|
68
|
+
toolName: {
|
|
69
|
+
type: "string",
|
|
70
|
+
description: "要调用的具体端工具名称,即get_xxx_tool_schema返回的工具的name",
|
|
71
|
+
},
|
|
72
|
+
arguments: {
|
|
73
|
+
type: "object",
|
|
74
|
+
description: "工具所需的具体参数JSON键值对",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
required: ["toolName", "arguments"],
|
|
78
|
+
},
|
|
79
|
+
async execute(toolCallId, params) {
|
|
80
|
+
const { toolName, arguments: toolArgs } = params;
|
|
81
|
+
// 向用户端发送具体工具名的状态更新
|
|
82
|
+
const ctx = getCurrentSessionContext();
|
|
83
|
+
if (ctx) {
|
|
84
|
+
const currentTaskId = getCurrentTaskId(ctx.sessionId) ?? ctx.taskId;
|
|
85
|
+
const currentMessageId = getCurrentMessageId(ctx.sessionId) ?? ctx.messageId;
|
|
86
|
+
try {
|
|
87
|
+
await sendStatusUpdate({
|
|
88
|
+
config: ctx.config,
|
|
89
|
+
sessionId: ctx.sessionId,
|
|
90
|
+
taskId: currentTaskId,
|
|
91
|
+
messageId: currentMessageId,
|
|
92
|
+
text: `正在使用工具: ${toolName}...`,
|
|
93
|
+
state: "working",
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
catch (_) {
|
|
97
|
+
// 状态更新失败不影响工具执行
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const tool = deviceToolRegistry.get(toolName);
|
|
101
|
+
if (!tool) {
|
|
102
|
+
return {
|
|
103
|
+
content: [
|
|
104
|
+
{
|
|
105
|
+
type: "text",
|
|
106
|
+
text: `端工具${toolName}不存在。请确保toolName为get_xxx_tool_schema返回的工具的name。`,
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
return await tool.execute(toolCallId, toolArgs);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
// ToolInputError (.name === "ToolInputError") 或其他参数校验错误
|
|
116
|
+
if (error.name === "ToolInputError") {
|
|
117
|
+
return {
|
|
118
|
+
content: [
|
|
119
|
+
{
|
|
120
|
+
type: "text",
|
|
121
|
+
text: `端工具参数错误:${error.message}。请确保arguments符合get_xxx_tool_schema返回的工具schema。`,
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// 非参数错误(网络超时等),直接向上抛出
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
};
|
|
@@ -17,18 +17,7 @@ export const createAlarmTool = {
|
|
|
17
17
|
name: "create_alarm",
|
|
18
18
|
label: "Create Alarm",
|
|
19
19
|
description: `在用户设备上创建闹钟。
|
|
20
|
-
|
|
21
|
-
必需参数:
|
|
22
|
-
- alarmTime: 闹钟时间,格式必须为:YYYYMMDD hhmmss(例如:20240315 143000,表示2024年3月15日14:30:00)
|
|
23
|
-
|
|
24
|
-
可选参数(针对用户没有提及的参数,如果有默认参数,则发送请求时使用默认参数):
|
|
25
|
-
- alarmTitle: 闹钟名称/标题,默认为"闹钟"
|
|
26
|
-
- alarmSnoozeDuration: 小睡间隔(分钟),枚举值:5,10,15,20,25,30,默认10
|
|
27
|
-
- alarmSnoozeTotal: 再响次数,枚举值:0,1,3,5,10,默认0(表示不再响)
|
|
28
|
-
- alarmRingDuration: 响铃时长(分钟),枚举值:1,5,10,15,20,30,默认5
|
|
29
|
-
- daysOfWakeType: 闹钟响铃类型,枚举值:0=单次响铃,1=法定节假日,2=每天,3=自定义时间,4=法定工作日,默认0
|
|
30
|
-
- daysOfWeek: 自定义响铃星期,仅当daysOfWakeType=3(自定义时间)时必需且有效,其他情况不要传递此参数。数组或JSON字符串,枚举值:Mon,Tues,Wed,Thur,Fri,Sat,Sun。
|
|
31
|
-
|
|
20
|
+
|
|
32
21
|
注意事项:
|
|
33
22
|
a. 操作超时时间为60秒,请勿重复调用此工具,如果超时或失败,最多重试一次。
|
|
34
23
|
b. 使用该工具之前需获取当前真实时间
|
|
@@ -39,7 +28,7 @@ b. 使用该工具之前需获取当前真实时间
|
|
|
39
28
|
properties: {
|
|
40
29
|
alarmTime: {
|
|
41
30
|
type: "string",
|
|
42
|
-
description: "闹钟时间,格式必须为:YYYYMMDD hhmmss(例如:20240315 143000)",
|
|
31
|
+
description: "闹钟时间,格式必须为:YYYYMMDD hhmmss(例如:20240315 143000,表示2024年3月15日14:30:00)",
|
|
43
32
|
},
|
|
44
33
|
alarmTitle: {
|
|
45
34
|
type: "string",
|
|
@@ -51,7 +40,7 @@ b. 使用该工具之前需获取当前真实时间
|
|
|
51
40
|
},
|
|
52
41
|
alarmSnoozeTotal: {
|
|
53
42
|
type: "number",
|
|
54
|
-
description: "再响次数,枚举值:0,1,3,5,10,默认0",
|
|
43
|
+
description: "再响次数,枚举值:0,1,3,5,10,默认0(表示不再响)",
|
|
55
44
|
},
|
|
56
45
|
alarmRingDuration: {
|
|
57
46
|
type: "number",
|
|
@@ -59,12 +48,12 @@ b. 使用该工具之前需获取当前真实时间
|
|
|
59
48
|
},
|
|
60
49
|
daysOfWakeType: {
|
|
61
50
|
type: "number",
|
|
62
|
-
description: "
|
|
51
|
+
description: "闹钟响铃类型,枚举值:0=单次响铃,1=法定节假日,2=每天,3=自定义时间,4=法定工作日,默认0",
|
|
63
52
|
},
|
|
64
53
|
daysOfWeek: {
|
|
65
54
|
// 不指定 type,允许传入数组或 JSON 字符串
|
|
66
55
|
// 具体的类型验证和转换在 execute 函数内部进行
|
|
67
|
-
description: "
|
|
56
|
+
description: "自定义响铃星期,仅当daysOfWakeType=3(自定义时间)时必需且有效,其他情况不要传递此参数。数组或JSON字符串,枚举值:Mon,Tues,Wed,Thur,Fri,Sat,Sun。",
|
|
68
57
|
},
|
|
69
58
|
},
|
|
70
59
|
required: ["alarmTime"],
|
|
@@ -16,9 +16,6 @@ export const deleteAlarmTool = {
|
|
|
16
16
|
label: "Delete Alarm",
|
|
17
17
|
description: `删除用户设备上的闹钟。使用前必须先调用 search_alarm 或 create_alarm 工具获取闹钟的 entityId。
|
|
18
18
|
|
|
19
|
-
工具参数:
|
|
20
|
-
- items: 要删除的闹钟列表,每个元素包含 entityId 字段。支持数组或 JSON 字符串格式。entityId 是闹钟的唯一标识符(从 search_alarm 或 create_alarm 工具获取)。
|
|
21
|
-
|
|
22
19
|
使用示例:
|
|
23
20
|
- 删除单个闹钟:{"items": [{"entityId": "6"}]}
|
|
24
21
|
- 删除多个闹钟:{"items": [{"entityId": "6"}, {"entityId": "8"}]}
|
|
@@ -35,7 +32,7 @@ export const deleteAlarmTool = {
|
|
|
35
32
|
items: {
|
|
36
33
|
// 不指定 type,允许传入数组或 JSON 字符串
|
|
37
34
|
// 具体的类型验证和转换在 execute 函数内部进行
|
|
38
|
-
description: "要删除的闹钟列表,每个元素包含 entityId 字段。支持数组或 JSON 字符串格式。",
|
|
35
|
+
description: "要删除的闹钟列表,每个元素包含 entityId 字段。支持数组或 JSON 字符串格式。entityId 是闹钟的唯一标识符(从 search_alarm 或 create_alarm 工具获取)。",
|
|
39
36
|
},
|
|
40
37
|
},
|
|
41
38
|
required: ["items"],
|
|
@@ -13,11 +13,12 @@ const DEVICE_TOOL_POLICY = {
|
|
|
13
13
|
"call_phone",
|
|
14
14
|
"send_message",
|
|
15
15
|
"search_message",
|
|
16
|
-
"send_command_to_car",
|
|
17
16
|
"search_contact",
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
17
|
+
"get_contact_tool_schema",
|
|
18
|
+
"query_collection",
|
|
19
|
+
"add_collection",
|
|
20
|
+
"delete_collection",
|
|
21
|
+
"get_collection_tool_schema",
|
|
21
22
|
],
|
|
22
23
|
},
|
|
23
24
|
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { getXYWebSocketManager } from "../client.js";
|
|
2
|
+
import { sendCommand } from "../formatter.js";
|
|
3
|
+
import { getCurrentSessionContext } from "./session-manager.js";
|
|
4
|
+
/**
|
|
5
|
+
* XY find PC devices tool - finds all PC devices associated with the user.
|
|
6
|
+
* Returns device IDs for use in subsequent file search operations.
|
|
7
|
+
*/
|
|
8
|
+
export const findPcDevicesTool = {
|
|
9
|
+
name: "find_pc_devices",
|
|
10
|
+
label: "Find PC Devices",
|
|
11
|
+
description: `查找用户所有PC/电脑设备,获取设备ID列表。当用户说"帮我找一下PC/电脑上的xxx文件"、"帮我搜索电脑上的xxx"等涉及PC设备的请求时,先调用此工具获取设备ID,再进行后续操作。注意:操作超时时间为60秒,请勿重复调用此工具,如果超时或失败,最多重试一次。回复约束:如果工具返回没有授权或者其他报错,只需要完整描述没有授权或者其他报错内容即可,不需要主动给用户提供解决方案,请严格遵守。`,
|
|
12
|
+
parameters: {
|
|
13
|
+
type: "object",
|
|
14
|
+
properties: {},
|
|
15
|
+
required: [],
|
|
16
|
+
},
|
|
17
|
+
async execute(toolCallId, params) {
|
|
18
|
+
// Get session context
|
|
19
|
+
const sessionContext = getCurrentSessionContext();
|
|
20
|
+
if (!sessionContext) {
|
|
21
|
+
throw new Error("No active XY session found. Find PC devices tool can only be used during an active conversation.");
|
|
22
|
+
}
|
|
23
|
+
const { config, sessionId, taskId, messageId } = sessionContext;
|
|
24
|
+
// Get WebSocket manager
|
|
25
|
+
const wsManager = getXYWebSocketManager(config);
|
|
26
|
+
// Build GetAllDevice command
|
|
27
|
+
const command = {
|
|
28
|
+
header: {
|
|
29
|
+
namespace: "Common",
|
|
30
|
+
name: "Action",
|
|
31
|
+
},
|
|
32
|
+
payload: {
|
|
33
|
+
cardParam: {},
|
|
34
|
+
executeParam: {
|
|
35
|
+
achieveType: "INTENT",
|
|
36
|
+
actionResponse: true,
|
|
37
|
+
bundleName: "com.huawei.hmos.aidispatchservice",
|
|
38
|
+
dimension: "",
|
|
39
|
+
executeMode: "background",
|
|
40
|
+
intentName: "GetAllDevice",
|
|
41
|
+
intentParam: {},
|
|
42
|
+
needUnlock: true,
|
|
43
|
+
permissionId: [],
|
|
44
|
+
timeOut: 5,
|
|
45
|
+
},
|
|
46
|
+
needUploadResult: true,
|
|
47
|
+
pageControlRelated: false,
|
|
48
|
+
responses: [{
|
|
49
|
+
displayText: "",
|
|
50
|
+
resultCode: "",
|
|
51
|
+
ttsText: "",
|
|
52
|
+
}],
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
// Send command and wait for response (60 second timeout)
|
|
56
|
+
return new Promise((resolve, reject) => {
|
|
57
|
+
const timeout = setTimeout(() => {
|
|
58
|
+
wsManager.off("data-event", handler);
|
|
59
|
+
reject(new Error("查找PC设备超时(60秒)"));
|
|
60
|
+
}, 60000);
|
|
61
|
+
// Listen for data events from WebSocket
|
|
62
|
+
const handler = (event) => {
|
|
63
|
+
if (event.intentName === "GetAllDevice") {
|
|
64
|
+
clearTimeout(timeout);
|
|
65
|
+
wsManager.off("data-event", handler);
|
|
66
|
+
if (event.status === "success" && event.outputs) {
|
|
67
|
+
resolve({
|
|
68
|
+
content: [
|
|
69
|
+
{
|
|
70
|
+
type: "text",
|
|
71
|
+
text: JSON.stringify(event.outputs),
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
reject(new Error(`查找PC设备失败: ${event.status}`));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
// Register event handler
|
|
82
|
+
wsManager.on("data-event", handler);
|
|
83
|
+
// Send the command
|
|
84
|
+
sendCommand({
|
|
85
|
+
config,
|
|
86
|
+
sessionId,
|
|
87
|
+
taskId,
|
|
88
|
+
messageId,
|
|
89
|
+
command,
|
|
90
|
+
}).then(() => {
|
|
91
|
+
}).catch((error) => {
|
|
92
|
+
clearTimeout(timeout);
|
|
93
|
+
wsManager.off("data-event", handler);
|
|
94
|
+
reject(error);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare const getAlarmToolSchemaTool: {
|
|
2
|
+
name: string;
|
|
3
|
+
label: string;
|
|
4
|
+
description: string;
|
|
5
|
+
parameters: {
|
|
6
|
+
type: "object";
|
|
7
|
+
properties: {};
|
|
8
|
+
required: string[];
|
|
9
|
+
};
|
|
10
|
+
execute(_toolCallId: string, _params: any): Promise<{
|
|
11
|
+
content: {
|
|
12
|
+
type: "text";
|
|
13
|
+
text: string;
|
|
14
|
+
}[];
|
|
15
|
+
}>;
|
|
16
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createSchemaTool } from "./schema-tool-factory.js";
|
|
2
|
+
import { createAlarmTool } from "./create-alarm-tool.js";
|
|
3
|
+
import { searchAlarmTool } from "./search-alarm-tool.js";
|
|
4
|
+
import { modifyAlarmTool } from "./modify-alarm-tool.js";
|
|
5
|
+
import { deleteAlarmTool } from "./delete-alarm-tool.js";
|
|
6
|
+
export const getAlarmToolSchemaTool = createSchemaTool({
|
|
7
|
+
name: "get_alarm_tool_schema",
|
|
8
|
+
label: "Get Alarm Tool Schema",
|
|
9
|
+
description: "获取可在用户设备上创建、检索、修改、删除闹钟的相关端工具列表。",
|
|
10
|
+
tools: [createAlarmTool, searchAlarmTool, modifyAlarmTool, deleteAlarmTool],
|
|
11
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare const getCalendarToolSchemaTool: {
|
|
2
|
+
name: string;
|
|
3
|
+
label: string;
|
|
4
|
+
description: string;
|
|
5
|
+
parameters: {
|
|
6
|
+
type: "object";
|
|
7
|
+
properties: {};
|
|
8
|
+
required: string[];
|
|
9
|
+
};
|
|
10
|
+
execute(_toolCallId: string, _params: any): Promise<{
|
|
11
|
+
content: {
|
|
12
|
+
type: "text";
|
|
13
|
+
text: string;
|
|
14
|
+
}[];
|
|
15
|
+
}>;
|
|
16
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createSchemaTool } from "./schema-tool-factory.js";
|
|
2
|
+
import { calendarTool } from "./calendar-tool.js";
|
|
3
|
+
import { searchCalendarTool } from "./search-calendar-tool.js";
|
|
4
|
+
export const getCalendarToolSchemaTool = createSchemaTool({
|
|
5
|
+
name: "get_calendar_tool_schema",
|
|
6
|
+
label: "Get Calendar Tool Schema",
|
|
7
|
+
description: "获取可在用户设备上创建、检索日程的相关端工具列表。",
|
|
8
|
+
tools: [calendarTool, searchCalendarTool],
|
|
9
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare const getCollectionToolSchemaTool: {
|
|
2
|
+
name: string;
|
|
3
|
+
label: string;
|
|
4
|
+
description: string;
|
|
5
|
+
parameters: {
|
|
6
|
+
type: "object";
|
|
7
|
+
properties: {};
|
|
8
|
+
required: string[];
|
|
9
|
+
};
|
|
10
|
+
execute(_toolCallId: string, _params: any): Promise<{
|
|
11
|
+
content: {
|
|
12
|
+
type: "text";
|
|
13
|
+
text: string;
|
|
14
|
+
}[];
|
|
15
|
+
}>;
|
|
16
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createSchemaTool } from "./schema-tool-factory.js";
|
|
2
|
+
import { xiaoyiAddCollectionTool } from "./xiaoyi-add-collection-tool.js";
|
|
3
|
+
import { xiaoyiCollectionTool } from "./xiaoyi-collection-tool.js";
|
|
4
|
+
import { xiaoyiDeleteCollectionTool } from "./xiaoyi-delete-collection-tool.js";
|
|
5
|
+
export const getCollectionToolSchemaTool = createSchemaTool({
|
|
6
|
+
name: "get_collection_tool_schema",
|
|
7
|
+
label: "Get Collection Tool Schema",
|
|
8
|
+
description: "获取可在用户设备上添加、检索、删除小艺收藏中的公共知识数据的相关端工具列表。",
|
|
9
|
+
tools: [xiaoyiAddCollectionTool, xiaoyiCollectionTool, xiaoyiDeleteCollectionTool],
|
|
10
|
+
});
|