@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/crossref.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.crossref.org";
|
|
6
6
|
|
|
@@ -23,7 +23,7 @@ export function createCrossRefTools(
|
|
|
23
23
|
description: "DOI to resolve, e.g. '10.1038/nature12373'",
|
|
24
24
|
}),
|
|
25
25
|
}),
|
|
26
|
-
execute: async (input: { doi: string }) => {
|
|
26
|
+
execute: async (_toolCallId: string, input: { doi: string }) => {
|
|
27
27
|
if (!input?.doi) {
|
|
28
28
|
return toolResult({ error: 'doi parameter is required (e.g., "10.1038/nature12373")' });
|
|
29
29
|
}
|
|
@@ -94,7 +94,7 @@ export function createCrossRefTools(
|
|
|
94
94
|
Type.Number({ description: "Max results (default 10, max 100)" }),
|
|
95
95
|
),
|
|
96
96
|
}),
|
|
97
|
-
execute: async (input: {
|
|
97
|
+
execute: async (_toolCallId: string, input: {
|
|
98
98
|
query: string;
|
|
99
99
|
journal?: string;
|
|
100
100
|
issn?: string;
|
|
@@ -105,8 +105,17 @@ export function createCrossRefTools(
|
|
|
105
105
|
sort?: string;
|
|
106
106
|
limit?: number;
|
|
107
107
|
}) => {
|
|
108
|
+
const query = validParam(input?.query);
|
|
109
|
+
if (!query) {
|
|
110
|
+
return toolResult({
|
|
111
|
+
error:
|
|
112
|
+
"query parameter is required and must not be empty. " +
|
|
113
|
+
"Example: search_crossref({ query: \"PFAS machine learning\", from_year: 2023 })",
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
108
117
|
const params = new URLSearchParams({
|
|
109
|
-
query
|
|
118
|
+
query,
|
|
110
119
|
rows: String(Math.min(input.limit ?? 10, 100)),
|
|
111
120
|
});
|
|
112
121
|
|
|
@@ -114,16 +123,20 @@ export function createCrossRefTools(
|
|
|
114
123
|
const filters: string[] = [];
|
|
115
124
|
if (input.from_year) filters.push(`from-pub-date:${input.from_year}`);
|
|
116
125
|
if (input.until_year) filters.push(`until-pub-date:${input.until_year}`);
|
|
117
|
-
|
|
126
|
+
const type = validParam(input.type);
|
|
127
|
+
if (type) filters.push(`type:${type}`);
|
|
118
128
|
if (input.has_abstract) filters.push("has-abstract:true");
|
|
119
|
-
|
|
129
|
+
const issn = validParam(input.issn);
|
|
130
|
+
if (issn) filters.push(`issn:${issn}`);
|
|
120
131
|
if (filters.length > 0) params.set("filter", filters.join(","));
|
|
121
132
|
|
|
122
133
|
// Journal name as query.container-title (separate from filter)
|
|
123
|
-
|
|
134
|
+
const journal = validParam(input.journal);
|
|
135
|
+
if (journal) params.set("query.container-title", journal);
|
|
124
136
|
|
|
125
|
-
|
|
126
|
-
|
|
137
|
+
const sort = validParam(input.sort);
|
|
138
|
+
if (sort) {
|
|
139
|
+
params.set("sort", sort);
|
|
127
140
|
params.set("order", "desc");
|
|
128
141
|
}
|
|
129
142
|
|
package/src/tools/datacite.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.datacite.org";
|
|
6
6
|
|
|
@@ -31,22 +31,31 @@ export function createDataCiteTools(
|
|
|
31
31
|
Type.Number({ description: "Published from this year onward" }),
|
|
32
32
|
),
|
|
33
33
|
}),
|
|
34
|
-
execute: async (input: {
|
|
34
|
+
execute: async (_toolCallId: string, input: {
|
|
35
35
|
query: string;
|
|
36
36
|
max_results?: number;
|
|
37
37
|
resource_type?: string;
|
|
38
38
|
from_year?: number;
|
|
39
39
|
}) => {
|
|
40
|
-
const
|
|
40
|
+
const query = validParam(input?.query);
|
|
41
|
+
if (!query) {
|
|
42
|
+
return toolResult({
|
|
43
|
+
error:
|
|
44
|
+
"query parameter is required and must not be empty. " +
|
|
45
|
+
"Example: search_datacite({ query: \"climate dataset\" })",
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const pageSize = Math.min(input?.max_results ?? 10, 100);
|
|
41
49
|
const params = new URLSearchParams({
|
|
42
|
-
query
|
|
50
|
+
query,
|
|
43
51
|
"page[size]": String(pageSize),
|
|
44
52
|
});
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
const resourceType = validParam(input.resource_type);
|
|
54
|
+
if (resourceType) {
|
|
55
|
+
params.set("resource-type-id", resourceType.toLowerCase());
|
|
47
56
|
}
|
|
48
|
-
if (input
|
|
49
|
-
params.set("query", `${
|
|
57
|
+
if (input?.from_year) {
|
|
58
|
+
params.set("query", `${query} AND publicationYear:[${input.from_year} TO *]`);
|
|
50
59
|
}
|
|
51
60
|
|
|
52
61
|
const tracked = await trackedFetch("datacite", `${BASE}/dois?${params}`, undefined, 15_000);
|
|
@@ -117,7 +126,7 @@ export function createDataCiteTools(
|
|
|
117
126
|
description: "DOI to resolve, e.g. '10.5281/zenodo.1234567'",
|
|
118
127
|
}),
|
|
119
128
|
}),
|
|
120
|
-
execute: async (input: { doi: string }) => {
|
|
129
|
+
execute: async (_toolCallId: string, input: { doi: string }) => {
|
|
121
130
|
if (!input?.doi) {
|
|
122
131
|
return toolResult({ error: 'doi parameter is required (e.g., "10.5281/zenodo.1234567")' });
|
|
123
132
|
}
|
package/src/tools/dblp.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
|
export function createDblpTools(
|
|
6
6
|
_ctx: OpenClawPluginToolContext,
|
|
@@ -23,13 +23,20 @@ export function createDblpTools(
|
|
|
23
23
|
Type.Number({ description: "Result offset for pagination" }),
|
|
24
24
|
),
|
|
25
25
|
}),
|
|
26
|
-
execute: async (input: {
|
|
26
|
+
execute: async (_toolCallId: string, input: {
|
|
27
27
|
query: string;
|
|
28
28
|
max_results?: number;
|
|
29
29
|
offset?: number;
|
|
30
30
|
}) => {
|
|
31
|
+
const query = validParam(input?.query);
|
|
32
|
+
if (!query) {
|
|
33
|
+
return toolResult({
|
|
34
|
+
error: "query parameter is required and must not be empty.",
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
31
38
|
const params = new URLSearchParams({
|
|
32
|
-
q:
|
|
39
|
+
q: query,
|
|
33
40
|
format: "json",
|
|
34
41
|
h: String(Math.min(input.max_results ?? 10, 1000)),
|
|
35
42
|
});
|
|
@@ -90,9 +97,16 @@ export function createDblpTools(
|
|
|
90
97
|
Type.Number({ description: "Max results (default 10)" }),
|
|
91
98
|
),
|
|
92
99
|
}),
|
|
93
|
-
execute: async (input: { query: string; max_results?: number }) => {
|
|
100
|
+
execute: async (_toolCallId: string, input: { query: string; max_results?: number }) => {
|
|
101
|
+
const query = validParam(input?.query);
|
|
102
|
+
if (!query) {
|
|
103
|
+
return toolResult({
|
|
104
|
+
error: "query parameter is required and must not be empty.",
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
94
108
|
const params = new URLSearchParams({
|
|
95
|
-
q:
|
|
109
|
+
q: query,
|
|
96
110
|
format: "json",
|
|
97
111
|
h: String(Math.min(input.max_results ?? 10, 100)),
|
|
98
112
|
});
|
package/src/tools/doaj.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://doaj.org/api";
|
|
6
6
|
|
|
@@ -29,17 +29,26 @@ export function createDoajTools(
|
|
|
29
29
|
Type.String({ description: "Sort field, e.g. 'created_date:desc'" }),
|
|
30
30
|
),
|
|
31
31
|
}),
|
|
32
|
-
execute: async (input: {
|
|
32
|
+
execute: async (_toolCallId: string, input: {
|
|
33
33
|
query: string;
|
|
34
34
|
max_results?: number;
|
|
35
35
|
page?: number;
|
|
36
36
|
sort?: string;
|
|
37
37
|
}) => {
|
|
38
|
-
const
|
|
39
|
-
|
|
38
|
+
const query = validParam(input?.query);
|
|
39
|
+
if (!query) {
|
|
40
|
+
return toolResult({
|
|
41
|
+
error:
|
|
42
|
+
"query parameter is required and must not be empty. " +
|
|
43
|
+
"Example: search_doaj({ query: \"open access genomics\" })",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const pageSize = Math.min(input?.max_results ?? 10, 100);
|
|
47
|
+
const page = input?.page ?? 1;
|
|
40
48
|
|
|
41
|
-
let url = `${BASE}/search/articles/${encodeURIComponent(
|
|
42
|
-
|
|
49
|
+
let url = `${BASE}/search/articles/${encodeURIComponent(query)}?page=${page}&pageSize=${pageSize}`;
|
|
50
|
+
const sort = validParam(input.sort);
|
|
51
|
+
if (sort) url += `&sort=${encodeURIComponent(sort)}`;
|
|
43
52
|
|
|
44
53
|
const tracked = await trackedFetch("doaj", url);
|
|
45
54
|
if (isTrackedError(tracked)) return tracked;
|
package/src/tools/europe-pmc.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://www.ebi.ac.uk/europepmc/webservices/rest";
|
|
6
6
|
|
|
@@ -31,20 +31,29 @@ export function createEuropePmcTools(
|
|
|
31
31
|
Type.String({ description: "Pagination cursor (use value from previous response)" }),
|
|
32
32
|
),
|
|
33
33
|
}),
|
|
34
|
-
execute: async (input: {
|
|
34
|
+
execute: async (_toolCallId: string, input: {
|
|
35
35
|
query: string;
|
|
36
36
|
max_results?: number;
|
|
37
37
|
sort?: string;
|
|
38
38
|
cursor?: string;
|
|
39
39
|
}) => {
|
|
40
|
+
const query = validParam(input?.query);
|
|
41
|
+
if (!query) {
|
|
42
|
+
return toolResult({
|
|
43
|
+
error:
|
|
44
|
+
"query parameter is required and must not be empty. " +
|
|
45
|
+
"Example: search_europe_pmc({ query: \"CRISPR gene editing\" })",
|
|
46
|
+
});
|
|
47
|
+
}
|
|
40
48
|
const params = new URLSearchParams({
|
|
41
|
-
query
|
|
49
|
+
query,
|
|
42
50
|
format: "json",
|
|
43
51
|
pageSize: String(Math.min(input.max_results ?? 10, 1000)),
|
|
44
52
|
resultType: "core",
|
|
45
53
|
});
|
|
46
|
-
|
|
47
|
-
params.set("
|
|
54
|
+
const sort = validParam(input.sort);
|
|
55
|
+
if (sort) params.set("sort", sort);
|
|
56
|
+
params.set("cursorMark", validParam(input.cursor) ?? "*");
|
|
48
57
|
|
|
49
58
|
const tracked = await trackedFetch("europe_pmc", `${BASE}/search?${params}`);
|
|
50
59
|
if (isTrackedError(tracked)) return tracked;
|
|
@@ -91,11 +100,14 @@ export function createEuropePmcTools(
|
|
|
91
100
|
),
|
|
92
101
|
page: Type.Optional(Type.Number({ description: "Page number (default 1)" })),
|
|
93
102
|
}),
|
|
94
|
-
execute: async (input: { pmid: string; max_results?: number; page?: number }) => {
|
|
103
|
+
execute: async (_toolCallId: string, input: { pmid: string; max_results?: number; page?: number }) => {
|
|
104
|
+
if (!input?.pmid) {
|
|
105
|
+
return toolResult({ error: 'pmid parameter is required (PubMed ID, e.g., "33116299")' });
|
|
106
|
+
}
|
|
95
107
|
const params = new URLSearchParams({
|
|
96
108
|
format: "json",
|
|
97
|
-
pageSize: String(input
|
|
98
|
-
page: String(input
|
|
109
|
+
pageSize: String(input?.max_results ?? 25),
|
|
110
|
+
page: String(input?.page ?? 1),
|
|
99
111
|
});
|
|
100
112
|
const tracked = await trackedFetch("europe_pmc", `${BASE}/MED/${input.pmid}/citations?${params}`);
|
|
101
113
|
if (isTrackedError(tracked)) return tracked;
|
|
@@ -129,10 +141,13 @@ export function createEuropePmcTools(
|
|
|
129
141
|
Type.Number({ description: "Max references to return (default 25)" }),
|
|
130
142
|
),
|
|
131
143
|
}),
|
|
132
|
-
execute: async (input: { pmid: string; max_results?: number }) => {
|
|
144
|
+
execute: async (_toolCallId: string, input: { pmid: string; max_results?: number }) => {
|
|
145
|
+
if (!input?.pmid) {
|
|
146
|
+
return toolResult({ error: 'pmid parameter is required (PubMed ID, e.g., "33116299")' });
|
|
147
|
+
}
|
|
133
148
|
const params = new URLSearchParams({
|
|
134
149
|
format: "json",
|
|
135
|
-
pageSize: String(input
|
|
150
|
+
pageSize: String(input?.max_results ?? 25),
|
|
136
151
|
});
|
|
137
152
|
const tracked = await trackedFetch("europe_pmc", `${BASE}/MED/${input.pmid}/references?${params}`);
|
|
138
153
|
if (isTrackedError(tracked)) return tracked;
|
package/src/tools/hal.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, validEnum } from "./util.js";
|
|
4
4
|
|
|
5
5
|
const BASE = "https://api.archives-ouvertes.fr";
|
|
6
6
|
|
|
@@ -49,15 +49,24 @@ export function createHalTools(
|
|
|
49
49
|
}),
|
|
50
50
|
),
|
|
51
51
|
}),
|
|
52
|
-
execute: async (input: {
|
|
52
|
+
execute: async (_toolCallId: string, input: {
|
|
53
53
|
query: string;
|
|
54
54
|
rows?: number;
|
|
55
55
|
sort?: string;
|
|
56
56
|
doc_type?: string;
|
|
57
57
|
}) => {
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
|
|
58
|
+
const query = validParam(input?.query);
|
|
59
|
+
if (!query) {
|
|
60
|
+
return toolResult({
|
|
61
|
+
error:
|
|
62
|
+
"query parameter is required and must not be empty. " +
|
|
63
|
+
"Example: search_hal({ query: \"machine learning\" })",
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
let q = query;
|
|
67
|
+
const docType = validParam(input.doc_type);
|
|
68
|
+
if (docType) {
|
|
69
|
+
q = `(${q}) AND docType_s:${docType}`;
|
|
61
70
|
}
|
|
62
71
|
|
|
63
72
|
const params = new URLSearchParams({
|
|
@@ -66,7 +75,8 @@ export function createHalTools(
|
|
|
66
75
|
rows: String(Math.min(input.rows ?? 10, 100)),
|
|
67
76
|
wt: "json",
|
|
68
77
|
});
|
|
69
|
-
|
|
78
|
+
const sort = validParam(input.sort);
|
|
79
|
+
if (sort) params.set("sort", sort);
|
|
70
80
|
|
|
71
81
|
const result = await trackedFetch(
|
|
72
82
|
"hal",
|
package/src/tools/inspire-hep.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, validEnum, validParam } from "./util.js";
|
|
4
4
|
|
|
5
5
|
const BASE = "https://inspirehep.net/api";
|
|
6
6
|
|
|
@@ -98,11 +98,19 @@ export function createInspireHepTools(
|
|
|
98
98
|
}),
|
|
99
99
|
),
|
|
100
100
|
}),
|
|
101
|
-
execute: async (input: { query: string; size?: number; sort?: string }) => {
|
|
101
|
+
execute: async (_toolCallId: string, input: { query: string; size?: number; sort?: string }) => {
|
|
102
|
+
const query = validParam(input?.query);
|
|
103
|
+
if (!query) {
|
|
104
|
+
return toolResult({
|
|
105
|
+
error: "query parameter is required and must not be empty.",
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const sort = validEnum(input.sort, ["mostrecent", "mostcited", "bestmatch"] as const, "bestmatch");
|
|
102
110
|
const params = new URLSearchParams({
|
|
103
|
-
q:
|
|
111
|
+
q: query,
|
|
104
112
|
size: String(Math.min(input.size ?? 10, 100)),
|
|
105
|
-
sort
|
|
113
|
+
sort,
|
|
106
114
|
});
|
|
107
115
|
|
|
108
116
|
const result = await trackedFetch(
|
|
@@ -136,7 +144,10 @@ export function createInspireHepTools(
|
|
|
136
144
|
"Paper identifier: arXiv ID (e.g. '1207.7214') or DOI (e.g. '10.1016/j.physletb.2012.08.020')",
|
|
137
145
|
}),
|
|
138
146
|
}),
|
|
139
|
-
execute: async (input: { identifier: string }) => {
|
|
147
|
+
execute: async (_toolCallId: string, input: { identifier: string }) => {
|
|
148
|
+
if (!input?.identifier) {
|
|
149
|
+
return toolResult({ error: 'identifier parameter is required (arXiv ID e.g. "1207.7214" or DOI e.g. "10.1016/j.physletb.2012.08.020")' });
|
|
150
|
+
}
|
|
140
151
|
// Determine if it's a DOI or arXiv ID
|
|
141
152
|
const id = input.identifier.trim();
|
|
142
153
|
let url: string;
|
package/src/tools/openaire.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.openaire.eu";
|
|
6
6
|
|
|
@@ -112,7 +112,7 @@ export function createOpenAireTools(
|
|
|
112
112
|
Type.Number({ description: "Max results (default 10, max 50)" }),
|
|
113
113
|
),
|
|
114
114
|
}),
|
|
115
|
-
execute: async (input: {
|
|
115
|
+
execute: async (_toolCallId: string, input: {
|
|
116
116
|
keywords: string;
|
|
117
117
|
author?: string;
|
|
118
118
|
doi?: string;
|
|
@@ -122,17 +122,30 @@ export function createOpenAireTools(
|
|
|
122
122
|
funder?: string;
|
|
123
123
|
max_results?: number;
|
|
124
124
|
}) => {
|
|
125
|
+
const keywords = validParam(input?.keywords);
|
|
126
|
+
if (!keywords) {
|
|
127
|
+
return toolResult({
|
|
128
|
+
error:
|
|
129
|
+
"keywords parameter is required and must not be empty. " +
|
|
130
|
+
"Example: search_openaire({ keywords: \"machine learning\" })",
|
|
131
|
+
});
|
|
132
|
+
}
|
|
125
133
|
const params = new URLSearchParams({
|
|
126
|
-
keywords
|
|
134
|
+
keywords,
|
|
127
135
|
format: "json",
|
|
128
136
|
size: String(Math.min(input.max_results ?? 10, 50)),
|
|
129
137
|
});
|
|
130
|
-
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
if (
|
|
138
|
+
const author = validParam(input.author);
|
|
139
|
+
if (author) params.set("author", author);
|
|
140
|
+
const doi = validParam(input.doi);
|
|
141
|
+
if (doi) params.set("doi", doi);
|
|
142
|
+
const fromDate = validParam(input.from_date);
|
|
143
|
+
if (fromDate) params.set("fromDateAccepted", fromDate);
|
|
144
|
+
const toDate = validParam(input.to_date);
|
|
145
|
+
if (toDate) params.set("toDateAccepted", toDate);
|
|
134
146
|
if (input.oa_only) params.set("OA", "true");
|
|
135
|
-
|
|
147
|
+
const funder = validParam(input.funder);
|
|
148
|
+
if (funder) params.set("funder", funder);
|
|
136
149
|
|
|
137
150
|
const tracked = await trackedFetch("openaire", `${BASE}/search/publications?${params}`, undefined, 15_000);
|
|
138
151
|
if (isTrackedError(tracked)) return tracked;
|
package/src/tools/openalex.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, validEnum } from "./util.js";
|
|
4
4
|
|
|
5
5
|
const BASE = "https://api.openalex.org";
|
|
6
6
|
|
|
@@ -38,7 +38,7 @@ export function createOpenAlexTools(
|
|
|
38
38
|
}),
|
|
39
39
|
),
|
|
40
40
|
}),
|
|
41
|
-
execute: async (input: {
|
|
41
|
+
execute: async (_toolCallId: string, input: {
|
|
42
42
|
query: string;
|
|
43
43
|
limit?: number;
|
|
44
44
|
from_year?: number;
|
|
@@ -46,17 +46,27 @@ export function createOpenAlexTools(
|
|
|
46
46
|
open_access?: boolean;
|
|
47
47
|
sort_by?: string;
|
|
48
48
|
}) => {
|
|
49
|
+
const query = validParam(input?.query);
|
|
50
|
+
if (!query) {
|
|
51
|
+
return toolResult({ error: "query parameter is required and must not be empty" });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const SORT_BY = ["cited_by_count", "publication_date", "relevance_score"] as const;
|
|
55
|
+
|
|
49
56
|
const filters: string[] = [];
|
|
50
57
|
if (input.from_year) filters.push(`from_publication_date:${input.from_year}-01-01`);
|
|
51
58
|
if (input.to_year) filters.push(`to_publication_date:${input.to_year}-12-31`);
|
|
52
59
|
if (input.open_access) filters.push("is_oa:true");
|
|
53
60
|
|
|
54
61
|
const params = new URLSearchParams({
|
|
55
|
-
search:
|
|
62
|
+
search: query,
|
|
56
63
|
per_page: String(Math.min(input.limit ?? 10, 200)),
|
|
57
64
|
});
|
|
58
65
|
if (filters.length > 0) params.set("filter", filters.join(","));
|
|
59
|
-
|
|
66
|
+
const sortBy = validParam(input.sort_by);
|
|
67
|
+
if (sortBy && (SORT_BY as readonly string[]).includes(sortBy)) {
|
|
68
|
+
params.set("sort", sortBy);
|
|
69
|
+
}
|
|
60
70
|
|
|
61
71
|
const tracked = await trackedFetch("openalex", `${BASE}/works?${params}`, { headers });
|
|
62
72
|
if (isTrackedError(tracked)) return tracked;
|
|
@@ -93,7 +103,7 @@ export function createOpenAlexTools(
|
|
|
93
103
|
"Work identifier: OpenAlex ID (e.g. 'W2741809807'), DOI URL, or PMID",
|
|
94
104
|
}),
|
|
95
105
|
}),
|
|
96
|
-
execute: async (input: { work_id: string }) => {
|
|
106
|
+
execute: async (_toolCallId: string, input: { work_id: string }) => {
|
|
97
107
|
if (!input?.work_id) {
|
|
98
108
|
return toolResult({ error: 'work_id parameter is required (e.g., "W2741809807" or a DOI like "10.1234/example")' });
|
|
99
109
|
}
|
|
@@ -137,7 +147,7 @@ export function createOpenAlexTools(
|
|
|
137
147
|
"Author identifier: OpenAlex ID (e.g. 'A5023888391'), ORCID, or name search",
|
|
138
148
|
}),
|
|
139
149
|
}),
|
|
140
|
-
execute: async (input: { author_id: string }) => {
|
|
150
|
+
execute: async (_toolCallId: string, input: { author_id: string }) => {
|
|
141
151
|
if (!input?.author_id) {
|
|
142
152
|
return toolResult({ error: 'author_id parameter is required (OpenAlex ID e.g. "A5023888391", ORCID, or author name)' });
|
|
143
153
|
}
|
|
@@ -28,7 +28,7 @@ export function createOpenCitationsTools(
|
|
|
28
28
|
description: "DOI of the paper, e.g. '10.1038/nature12373'",
|
|
29
29
|
}),
|
|
30
30
|
}),
|
|
31
|
-
execute: async (input: { doi: string }) => {
|
|
31
|
+
execute: async (_toolCallId: string, input: { doi: string }) => {
|
|
32
32
|
if (!input?.doi) {
|
|
33
33
|
return toolResult({ error: 'doi parameter is required (e.g., "10.1038/nature12373")' });
|
|
34
34
|
}
|
|
@@ -60,7 +60,7 @@ export function createOpenCitationsTools(
|
|
|
60
60
|
description: "DOI of the paper, e.g. '10.1038/nature12373'",
|
|
61
61
|
}),
|
|
62
62
|
}),
|
|
63
|
-
execute: async (input: { doi: string }) => {
|
|
63
|
+
execute: async (_toolCallId: string, input: { doi: string }) => {
|
|
64
64
|
if (!input?.doi) {
|
|
65
65
|
return toolResult({ error: 'doi parameter is required (e.g., "10.1038/nature12373")' });
|
|
66
66
|
}
|
|
@@ -92,7 +92,7 @@ export function createOpenCitationsTools(
|
|
|
92
92
|
description: "DOI of the paper, e.g. '10.1038/nature12373'",
|
|
93
93
|
}),
|
|
94
94
|
}),
|
|
95
|
-
execute: async (input: { doi: string }) => {
|
|
95
|
+
execute: async (_toolCallId: string, input: { doi: string }) => {
|
|
96
96
|
if (!input?.doi) {
|
|
97
97
|
return toolResult({ error: 'doi parameter is required (e.g., "10.1038/nature12373")' });
|
|
98
98
|
}
|
package/src/tools/orcid.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://pub.orcid.org/v3.0";
|
|
6
6
|
|
|
@@ -27,9 +27,16 @@ export function createOrcidTools(
|
|
|
27
27
|
Type.Number({ description: "Max results (default 10, max 100)" }),
|
|
28
28
|
),
|
|
29
29
|
}),
|
|
30
|
-
execute: async (input: { query: string; limit?: number }) => {
|
|
30
|
+
execute: async (_toolCallId: string, input: { query: string; limit?: number }) => {
|
|
31
|
+
const query = validParam(input?.query);
|
|
32
|
+
if (!query) {
|
|
33
|
+
return toolResult({
|
|
34
|
+
error: "query parameter is required and must not be empty.",
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
31
38
|
const params = new URLSearchParams({
|
|
32
|
-
q:
|
|
39
|
+
q: query,
|
|
33
40
|
rows: String(Math.min(input.limit ?? 10, 100)),
|
|
34
41
|
});
|
|
35
42
|
|
|
@@ -71,7 +78,7 @@ export function createOrcidTools(
|
|
|
71
78
|
description: "ORCID iD, e.g. '0000-0002-1825-0097'",
|
|
72
79
|
}),
|
|
73
80
|
}),
|
|
74
|
-
execute: async (input: { orcid: string }) => {
|
|
81
|
+
execute: async (_toolCallId: string, input: { orcid: string }) => {
|
|
75
82
|
if (!input?.orcid) {
|
|
76
83
|
return toolResult({ error: 'orcid parameter is required (e.g., "0000-0002-1825-0097")' });
|
|
77
84
|
}
|
|
@@ -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.osf.io/v2";
|
|
6
6
|
|
|
@@ -28,16 +28,17 @@ export function createOsfPreprintsTools(
|
|
|
28
28
|
Type.Number({ description: "Page number (default 1)" }),
|
|
29
29
|
),
|
|
30
30
|
}),
|
|
31
|
-
execute: async (input: {
|
|
31
|
+
execute: async (_toolCallId: string, input: {
|
|
32
32
|
provider?: string;
|
|
33
33
|
size?: number;
|
|
34
34
|
page?: number;
|
|
35
35
|
}) => {
|
|
36
36
|
const params = new URLSearchParams({
|
|
37
|
-
"page[size]": String(Math.min(input
|
|
37
|
+
"page[size]": String(Math.min(input?.size ?? 10, 100)),
|
|
38
38
|
});
|
|
39
|
-
|
|
40
|
-
if (
|
|
39
|
+
const provider = validParam(input?.provider);
|
|
40
|
+
if (provider) params.set("filter[provider]", provider);
|
|
41
|
+
if (input?.page) params.set("page", String(input.page));
|
|
41
42
|
|
|
42
43
|
const result = await trackedFetch(
|
|
43
44
|
"osf_preprints",
|
|
@@ -57,7 +58,7 @@ export function createOsfPreprintsTools(
|
|
|
57
58
|
|
|
58
59
|
return toolResult({
|
|
59
60
|
total_results: linksMeta?.total,
|
|
60
|
-
page: input
|
|
61
|
+
page: input?.page ?? 1,
|
|
61
62
|
source: "osf_preprints",
|
|
62
63
|
preprints: (items ?? []).map((item) => {
|
|
63
64
|
const attrs = item.attributes as Record<string, unknown> | undefined;
|
package/src/tools/pubmed.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, validEnum } from "./util.js";
|
|
4
4
|
|
|
5
5
|
const EUTILS = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils";
|
|
6
6
|
|
|
@@ -34,24 +34,37 @@ export function createPubMedTools(
|
|
|
34
34
|
Type.String({ description: "Max publication date (YYYY/MM/DD)" }),
|
|
35
35
|
),
|
|
36
36
|
}),
|
|
37
|
-
execute: async (input: {
|
|
37
|
+
execute: async (_toolCallId: string, input: {
|
|
38
38
|
query: string;
|
|
39
39
|
max_results?: number;
|
|
40
40
|
sort?: string;
|
|
41
41
|
min_date?: string;
|
|
42
42
|
max_date?: string;
|
|
43
43
|
}) => {
|
|
44
|
+
const query = validParam(input?.query);
|
|
45
|
+
if (!query) {
|
|
46
|
+
return toolResult({
|
|
47
|
+
error:
|
|
48
|
+
"query parameter is required and must not be empty. " +
|
|
49
|
+
"Example: search_pubmed({ query: \"CRISPR[Title] AND 2024[PDAT]\" })",
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const sort = validEnum(input.sort, ["relevance", "pub_date"] as const, "relevance");
|
|
54
|
+
const minDate = validParam(input.min_date);
|
|
55
|
+
const maxDate = validParam(input.max_date);
|
|
56
|
+
|
|
44
57
|
const searchParams = new URLSearchParams({
|
|
45
58
|
db: "pubmed",
|
|
46
|
-
term:
|
|
59
|
+
term: query,
|
|
47
60
|
retmax: String(Math.min(input.max_results ?? 10, 100)),
|
|
48
61
|
retmode: "json",
|
|
49
|
-
sort
|
|
62
|
+
sort,
|
|
50
63
|
usehistory: "y",
|
|
51
64
|
});
|
|
52
|
-
if (
|
|
53
|
-
if (
|
|
54
|
-
if (
|
|
65
|
+
if (minDate) searchParams.set("mindate", minDate);
|
|
66
|
+
if (maxDate) searchParams.set("maxdate", maxDate);
|
|
67
|
+
if (minDate || maxDate) searchParams.set("datetype", "pdat");
|
|
55
68
|
|
|
56
69
|
const searchTracked = await trackedFetch("pubmed", `${EUTILS}/esearch.fcgi?${searchParams}`);
|
|
57
70
|
if (isTrackedError(searchTracked)) return searchTracked;
|
|
@@ -108,7 +121,7 @@ export function createPubMedTools(
|
|
|
108
121
|
parameters: Type.Object({
|
|
109
122
|
pmid: Type.String({ description: "PubMed ID (numeric string)" }),
|
|
110
123
|
}),
|
|
111
|
-
execute: async (input: { pmid: string }) => {
|
|
124
|
+
execute: async (_toolCallId: string, input: { pmid: string }) => {
|
|
112
125
|
if (!input?.pmid) {
|
|
113
126
|
return toolResult({ error: 'pmid parameter is required (numeric PubMed ID, e.g., "33116299")' });
|
|
114
127
|
}
|