@wentorai/research-plugins 1.4.2 → 1.4.4
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/README.en.md +143 -0
- package/README.md +98 -131
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/literature/search/arxiv-api/SKILL.md +11 -2
- package/src/tools/arxiv.ts +19 -6
- package/src/tools/biorxiv.ts +17 -15
- package/src/tools/crossref.ts +22 -9
- package/src/tools/datacite.ts +18 -9
- package/src/tools/dblp.ts +19 -5
- package/src/tools/doaj.ts +15 -6
- package/src/tools/europe-pmc.ts +25 -10
- package/src/tools/hal.ts +16 -6
- package/src/tools/inspire-hep.ts +16 -5
- package/src/tools/openaire.ts +21 -8
- package/src/tools/openalex.ts +16 -6
- package/src/tools/opencitations.ts +3 -3
- package/src/tools/orcid.ts +11 -4
- package/src/tools/osf-preprints.ts +7 -6
- package/src/tools/pubmed.ts +21 -8
- package/src/tools/ror.ts +10 -3
- package/src/tools/unpaywall.ts +1 -1
- package/src/tools/util.ts +33 -0
- package/src/tools/zenodo.ts +18 -7
package/src/tools/ror.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import type { OpenClawPluginApi, OpenClawPluginToolContext } from "openclaw/plugin-sdk";
|
|
3
|
-
import { toolResult, trackedFetch, isTrackedError } from "./util.js";
|
|
3
|
+
import { toolResult, trackedFetch, isTrackedError, validParam } from "./util.js";
|
|
4
4
|
|
|
5
5
|
const BASE = "https://api.ror.org/v2";
|
|
6
6
|
|
|
@@ -22,9 +22,16 @@ export function createRorTools(
|
|
|
22
22
|
Type.Number({ description: "Max results (default 10, max 50)" }),
|
|
23
23
|
),
|
|
24
24
|
}),
|
|
25
|
-
execute: async (input: { query: string; max_results?: number }) => {
|
|
25
|
+
execute: async (_toolCallId: string, input: { query: string; max_results?: number }) => {
|
|
26
|
+
const query = validParam(input?.query);
|
|
27
|
+
if (!query) {
|
|
28
|
+
return toolResult({
|
|
29
|
+
error: "query parameter is required and must not be empty.",
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
26
33
|
const params = new URLSearchParams({
|
|
27
|
-
query
|
|
34
|
+
query,
|
|
28
35
|
});
|
|
29
36
|
|
|
30
37
|
const tracked = await trackedFetch("ror", `${BASE}/organizations?${params}`, undefined, 10_000);
|
package/src/tools/unpaywall.ts
CHANGED
|
@@ -21,7 +21,7 @@ export function createUnpaywallTools(
|
|
|
21
21
|
description: "DOI of the paper, e.g. '10.1038/nature12373'",
|
|
22
22
|
}),
|
|
23
23
|
}),
|
|
24
|
-
execute: async (input: { doi: string }) => {
|
|
24
|
+
execute: async (_toolCallId: string, input: { doi: string }) => {
|
|
25
25
|
if (!input?.doi) {
|
|
26
26
|
return toolResult({ error: 'doi parameter is required (e.g., "10.1038/nature12373")' });
|
|
27
27
|
}
|
package/src/tools/util.ts
CHANGED
|
@@ -150,3 +150,36 @@ export function isTrackedError(
|
|
|
150
150
|
): result is ReturnType<typeof toolResult> {
|
|
151
151
|
return "content" in result && "details" in result;
|
|
152
152
|
}
|
|
153
|
+
|
|
154
|
+
// ── LLM Input Sanitization ──────────────────────────────────────────────
|
|
155
|
+
|
|
156
|
+
const INVALID_PARAM_VALUES = new Set(["undefined", "null", "none", "None", ""]);
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Sanitize an optional string parameter from LLM tool calls.
|
|
160
|
+
*
|
|
161
|
+
* LLMs (especially weaker models) sometimes pass the literal string "undefined"
|
|
162
|
+
* or "null" instead of omitting the parameter. The nullish coalescing operator
|
|
163
|
+
* (`??`) does NOT catch these because they are truthy strings.
|
|
164
|
+
*
|
|
165
|
+
* Returns the value if valid, or `undefined` if it's a known non-value.
|
|
166
|
+
*/
|
|
167
|
+
export function validParam(value: string | undefined | null): string | undefined {
|
|
168
|
+
if (value == null) return undefined;
|
|
169
|
+
if (INVALID_PARAM_VALUES.has(value.trim())) return undefined;
|
|
170
|
+
return value;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Validate a string parameter against a whitelist of allowed values.
|
|
175
|
+
* Returns the value if it matches, or the fallback otherwise.
|
|
176
|
+
*/
|
|
177
|
+
export function validEnum<T extends string>(
|
|
178
|
+
value: string | undefined | null,
|
|
179
|
+
allowed: readonly T[],
|
|
180
|
+
fallback: T,
|
|
181
|
+
): T {
|
|
182
|
+
const clean = validParam(value);
|
|
183
|
+
if (clean && (allowed as readonly string[]).includes(clean)) return clean as T;
|
|
184
|
+
return fallback;
|
|
185
|
+
}
|
package/src/tools/zenodo.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import type { OpenClawPluginApi, OpenClawPluginToolContext } from "openclaw/plugin-sdk";
|
|
3
|
-
import { toolResult, trackedFetch, isTrackedError } from "./util.js";
|
|
3
|
+
import { toolResult, trackedFetch, isTrackedError, validParam } from "./util.js";
|
|
4
4
|
|
|
5
5
|
const BASE = "https://zenodo.org/api";
|
|
6
6
|
|
|
@@ -37,20 +37,31 @@ export function createZenodoTools(
|
|
|
37
37
|
}),
|
|
38
38
|
),
|
|
39
39
|
}),
|
|
40
|
-
execute: async (input: {
|
|
40
|
+
execute: async (_toolCallId: string, input: {
|
|
41
41
|
query: string;
|
|
42
42
|
type?: string;
|
|
43
43
|
size?: number;
|
|
44
44
|
sort?: string;
|
|
45
45
|
access_right?: string;
|
|
46
46
|
}) => {
|
|
47
|
+
const query = validParam(input?.query);
|
|
48
|
+
if (!query) {
|
|
49
|
+
return toolResult({
|
|
50
|
+
error:
|
|
51
|
+
"query parameter is required and must not be empty. " +
|
|
52
|
+
"Example: search_zenodo({ query: \"climate dataset\" })",
|
|
53
|
+
});
|
|
54
|
+
}
|
|
47
55
|
const params = new URLSearchParams({
|
|
48
|
-
q:
|
|
56
|
+
q: query,
|
|
49
57
|
size: String(Math.min(input.size ?? 10, 100)),
|
|
50
58
|
});
|
|
51
|
-
|
|
52
|
-
if (
|
|
53
|
-
|
|
59
|
+
const type = validParam(input.type);
|
|
60
|
+
if (type) params.set("type", type);
|
|
61
|
+
const sort = validParam(input.sort);
|
|
62
|
+
if (sort) params.set("sort", sort);
|
|
63
|
+
const accessRight = validParam(input.access_right);
|
|
64
|
+
if (accessRight) params.set("access_right", accessRight);
|
|
54
65
|
|
|
55
66
|
const result = await trackedFetch("zenodo", `${BASE}/records?${params}`, undefined, 10_000);
|
|
56
67
|
if (isTrackedError(result)) return result;
|
|
@@ -102,7 +113,7 @@ export function createZenodoTools(
|
|
|
102
113
|
description: "Zenodo record ID (numeric), e.g. '7042164'",
|
|
103
114
|
}),
|
|
104
115
|
}),
|
|
105
|
-
execute: async (input: { record_id: string }) => {
|
|
116
|
+
execute: async (_toolCallId: string, input: { record_id: string }) => {
|
|
106
117
|
if (!input?.record_id) {
|
|
107
118
|
return toolResult({ error: 'record_id parameter is required (e.g., "1234567")' });
|
|
108
119
|
}
|