@inflexa-ai/harness 0.13.0 → 0.14.1
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/agents/conversation-agent.js +23 -16
- package/dist/agents/sandbox/cheminformatics-agent.js +1 -1
- package/dist/agents/sandbox/chromatin-agent.js +1 -1
- package/dist/agents/sandbox/dna-methylation-agent.js +1 -1
- package/dist/agents/sandbox/drug-repurposing-agent.js +5 -10
- package/dist/agents/sandbox/enrichment-agent.js +1 -1
- package/dist/agents/sandbox/genomic-variant-agent.js +1 -11
- package/dist/agents/sandbox/immune-profiling-agent.js +1 -1
- package/dist/agents/sandbox/multi-omics-integration-agent.js +1 -1
- package/dist/agents/sandbox/network-agent.js +1 -1
- package/dist/agents/sandbox/pkpd-clinical-response-agent.js +3 -6
- package/dist/agents/sandbox/scientific-executor.js +1 -1
- package/dist/agents/sandbox/shared.js +7 -13
- package/dist/agents/sandbox/translational-safety-agent.js +4 -7
- package/dist/agents/sandbox/types.d.ts +5 -5
- package/dist/agents/sandbox/types.js +4 -4
- package/dist/app/chat-turn.js +4 -1
- package/dist/app/message-assembly.d.ts +5 -2
- package/dist/app/message-assembly.js +7 -2
- package/dist/app/run-activity.d.ts +13 -0
- package/dist/app/run-activity.js +51 -0
- package/dist/contracts/target-dossier.d.ts +10 -10
- package/dist/execution/report-runner.js +1 -1
- package/dist/execution/run-event-parts.d.ts +44 -0
- package/dist/execution/run-event-parts.js +0 -0
- package/dist/execution/run-event-stream.d.ts +91 -0
- package/dist/execution/run-event-stream.js +212 -0
- package/dist/execution/run-synthesis.js +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +20 -1
- package/dist/input-sanitization.d.ts +14 -4
- package/dist/input-sanitization.js +27 -4
- package/dist/lib/storage.js +4 -1
- package/dist/loop/budget-exceeded.d.ts +18 -2
- package/dist/loop/budget-exceeded.js +49 -2
- package/dist/loop/run-agent.d.ts +22 -1
- package/dist/loop/run-agent.js +91 -25
- package/dist/loop/run-to-terminal.d.ts +22 -4
- package/dist/loop/run-to-terminal.js +20 -4
- package/dist/memory/ai-sdk-message-storage.d.ts +37 -4
- package/dist/memory/ai-sdk-message-storage.js +45 -4
- package/dist/memory/content-to-cortex.js +19 -3
- package/dist/memory/message-backfill.js +1 -1
- package/dist/memory/thread-history.js +34 -8
- package/dist/prompts/conversation.d.ts +1 -1
- package/dist/prompts/conversation.js +15 -6
- package/dist/prompts/literature-reviewer.d.ts +1 -1
- package/dist/prompts/literature-reviewer.js +16 -14
- package/dist/prompts/planner.js +15 -20
- package/dist/prompts/sandbox/drug-repurposing-agent.d.ts +1 -1
- package/dist/prompts/sandbox/drug-repurposing-agent.js +31 -22
- package/dist/prompts/sandbox/multi-omics-integration-agent.d.ts +1 -1
- package/dist/prompts/sandbox/multi-omics-integration-agent.js +9 -10
- package/dist/prompts/sandbox/pkpd-clinical-response-agent.d.ts +1 -1
- package/dist/prompts/sandbox/pkpd-clinical-response-agent.js +8 -7
- package/dist/prompts/sandbox/translational-safety-agent.d.ts +1 -1
- package/dist/prompts/sandbox/translational-safety-agent.js +20 -14
- package/dist/prompts/synthesis-agent.d.ts +1 -1
- package/dist/prompts/synthesis-agent.js +7 -7
- package/dist/providers/errors.d.ts +7 -0
- package/dist/providers/errors.js +88 -3
- package/dist/runtime/pools.d.ts +15 -1
- package/dist/runtime/pools.js +15 -1
- package/dist/sandbox/sandbox-step-translate.d.ts +15 -2
- package/dist/sandbox/sandbox-step-translate.js +17 -2
- package/dist/schemas/workflow-state.d.ts +2 -2
- package/dist/schemas/workflow-state.js +2 -2
- package/dist/state/data-profile.d.ts +53 -1
- package/dist/state/data-profile.js +66 -6
- package/dist/state/index.d.ts +3 -3
- package/dist/state/index.js +2 -2
- package/dist/state/init.js +37 -2
- package/dist/state/runs.d.ts +39 -0
- package/dist/state/runs.js +103 -0
- package/dist/tasks/data-profile-activity.d.ts +87 -0
- package/dist/tasks/data-profile-activity.js +56 -0
- package/dist/tasks/data-profile.js +66 -4
- package/dist/tools/bio/chembl.d.ts +1 -1
- package/dist/tools/bio/chembl.js +51 -54
- package/dist/tools/bio/comptox.js +35 -33
- package/dist/tools/bio/drug-gene-interactions.d.ts +76 -0
- package/dist/tools/bio/drug-gene-interactions.js +175 -0
- package/dist/tools/bio/gene-disease-evidence.d.ts +61 -0
- package/dist/tools/bio/gene-disease-evidence.js +187 -0
- package/dist/tools/bio/gene-preclinical-profile.d.ts +58 -0
- package/dist/tools/bio/gene-preclinical-profile.js +125 -0
- package/dist/tools/bio/index.d.ts +23 -16
- package/dist/tools/bio/index.js +29 -22
- package/dist/tools/bio/keys.d.ts +16 -9
- package/dist/tools/bio/keys.js +22 -13
- package/dist/tools/bio/lookup-annotation.d.ts +48 -0
- package/dist/tools/bio/lookup-annotation.js +173 -0
- package/dist/tools/bio/opentargets.d.ts +7 -11
- package/dist/tools/bio/opentargets.js +24 -35
- package/dist/tools/bio/pubchem.js +6 -4
- package/dist/tools/bio/pubmed.d.ts +15 -7
- package/dist/tools/bio/pubmed.js +79 -20
- package/dist/tools/bio/search-clinical-trials.d.ts +20 -1
- package/dist/tools/bio/search-clinical-trials.js +26 -5
- package/dist/tools/bio/search-faers.js +9 -3
- package/dist/tools/bio/search-gene.d.ts +0 -1
- package/dist/tools/bio/search-gene.js +4 -5
- package/dist/tools/bio/search-geo-datasets.js +10 -4
- package/dist/tools/bio/search-interactions.d.ts +8 -4
- package/dist/tools/bio/search-interactions.js +28 -12
- package/dist/tools/bio/target-safety.d.ts +59 -0
- package/dist/tools/bio/target-safety.js +191 -0
- package/dist/tools/execute-analysis.d.ts +1 -0
- package/dist/tools/execute-analysis.js +5 -5
- package/dist/tools/lib/dgidb-client.d.ts +49 -0
- package/dist/tools/lib/dgidb-client.js +168 -0
- package/dist/tools/lib/disgenet-client.d.ts +56 -0
- package/dist/tools/lib/disgenet-client.js +59 -0
- package/dist/tools/lib/drugbank-client.d.ts +83 -0
- package/dist/tools/lib/drugbank-client.js +95 -0
- package/dist/tools/lib/ensembl-client.d.ts +4 -1
- package/dist/tools/lib/ensembl-client.js +5 -3
- package/dist/tools/lib/gwas-catalog-client.d.ts +60 -0
- package/dist/tools/lib/gwas-catalog-client.js +136 -0
- package/dist/tools/lib/pharmgkb-client.d.ts +22 -0
- package/dist/tools/lib/pharmgkb-client.js +32 -0
- package/dist/tools/lib/pubmed-client.d.ts +57 -4
- package/dist/tools/lib/pubmed-client.js +57 -3
- package/dist/tools/lib/string-client.d.ts +25 -4
- package/dist/tools/lib/string-client.js +22 -5
- package/dist/tools/research/generate-plan.d.ts +16 -9
- package/dist/tools/research/generate-plan.js +405 -85
- package/dist/tools/research/inspect-data-profile.d.ts +31 -6
- package/dist/tools/research/inspect-data-profile.js +23 -7
- package/dist/tools/research/inspect-run.d.ts +33 -15
- package/dist/tools/research/inspect-run.js +155 -66
- package/dist/tools/research/literature-reviewer.js +7 -11
- package/dist/workflows/execute-analysis.d.ts +63 -0
- package/dist/workflows/execute-analysis.js +73 -2
- package/dist/workflows/sandbox-step.js +1 -11
- package/dist/workflows/target-assessment/decisions/index.d.ts +1 -1
- package/dist/workflows/target-assessment/phase4-assemble.d.ts +1 -1
- package/dist/workflows/target-assessment/steps/phase2-aggregate.d.ts +2 -2
- package/dist/workflows/target-assessment/steps/phase3-aggregate.d.ts +1 -1
- package/package.json +1 -1
- package/dist/tools/bio/check-safety-panel.d.ts +0 -35
- package/dist/tools/bio/check-safety-panel.js +0 -76
- package/dist/tools/bio/get-impc-ko-profile.d.ts +0 -9
- package/dist/tools/bio/get-impc-ko-profile.js +0 -23
- package/dist/tools/bio/lookup-go-term.d.ts +0 -31
- package/dist/tools/bio/lookup-go-term.js +0 -116
- package/dist/tools/bio/search-bgee-expression.d.ts +0 -10
- package/dist/tools/bio/search-bgee-expression.js +0 -30
- package/dist/tools/bio/search-clinvar.d.ts +0 -13
- package/dist/tools/bio/search-clinvar.js +0 -28
- package/dist/tools/bio/search-dgidb.d.ts +0 -53
- package/dist/tools/bio/search-dgidb.js +0 -213
- package/dist/tools/bio/search-disgenet.d.ts +0 -30
- package/dist/tools/bio/search-disgenet.js +0 -84
- package/dist/tools/bio/search-drugbank.d.ts +0 -38
- package/dist/tools/bio/search-drugbank.js +0 -109
- package/dist/tools/bio/search-gwas-catalog.d.ts +0 -33
- package/dist/tools/bio/search-gwas-catalog.js +0 -172
- package/dist/tools/bio/search-pathway.d.ts +0 -12
- package/dist/tools/bio/search-pathway.js +0 -32
- package/dist/tools/bio/search-pharmgkb.d.ts +0 -17
- package/dist/tools/bio/search-pharmgkb.js +0 -52
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* searchDgidb tool — query drug-gene interactions via DGIdb GraphQL.
|
|
3
|
-
*
|
|
4
|
-
* DGIdb aggregates interactions from 30+ sources (ChEMBL, DrugBank, TTD,
|
|
5
|
-
* PharmGKB, GuideToPharmacology, CIViC, etc.). Free, no API key required.
|
|
6
|
-
*
|
|
7
|
-
* Two directions:
|
|
8
|
-
* - searchType="gene": input HUGO symbols, returns drugs interacting with each.
|
|
9
|
-
* - searchType="drug": input drug names/concept IDs, returns genes each acts on.
|
|
10
|
-
*/
|
|
11
|
-
import { ok } from "neverthrow";
|
|
12
|
-
import { z } from "zod";
|
|
13
|
-
import { defineTool } from "../define-tool.js";
|
|
14
|
-
import { createNoopLogger } from "../../lib/console-logger.js";
|
|
15
|
-
import { apiFetchValidated, describeApiError } from "../lib/api-utils.js";
|
|
16
|
-
const DGIDB_GRAPHQL_URL = "https://dgidb.org/api/graphql";
|
|
17
|
-
const GENE_INTERACTIONS_QUERY = `query($genes: [String!]!) {
|
|
18
|
-
genes(names: $genes) {
|
|
19
|
-
nodes {
|
|
20
|
-
name
|
|
21
|
-
interactions {
|
|
22
|
-
interactionScore
|
|
23
|
-
interactionTypes { type directionality }
|
|
24
|
-
drug { name conceptId }
|
|
25
|
-
interactionAttributes { name value }
|
|
26
|
-
publications { pmid }
|
|
27
|
-
sources { sourceDbName }
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}`;
|
|
32
|
-
const DRUG_INTERACTIONS_QUERY = `query($drugs: [String!]!) {
|
|
33
|
-
drugs(names: $drugs) {
|
|
34
|
-
nodes {
|
|
35
|
-
name
|
|
36
|
-
conceptId
|
|
37
|
-
interactions {
|
|
38
|
-
interactionScore
|
|
39
|
-
interactionTypes { type directionality }
|
|
40
|
-
gene { name }
|
|
41
|
-
interactionAttributes { name value }
|
|
42
|
-
publications { pmid }
|
|
43
|
-
sources { sourceDbName }
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}`;
|
|
48
|
-
// Shape of the DGIdb GraphQL payload as it arrives on the wire. Validated at
|
|
49
|
-
// the fetch boundary; `mapInteraction` normalizes each record into the
|
|
50
|
-
// `Interaction` we return. Most fields are optional/nullable to match GraphQL's
|
|
51
|
-
// per-field nullability — but `RawNode.name` is required because we key the
|
|
52
|
-
// node map on `name.toLowerCase()`; a node missing it is a contract break we
|
|
53
|
-
// surface as an `invalid_response` error rather than a runtime TypeError.
|
|
54
|
-
const RawInteractionSchema = z.object({
|
|
55
|
-
interactionScore: z.number().nullable().optional(),
|
|
56
|
-
interactionTypes: z.array(z.object({ type: z.string().optional(), directionality: z.string().nullable().optional() })).optional(),
|
|
57
|
-
drug: z.object({ name: z.string().optional(), conceptId: z.string().nullable().optional() }).nullable().optional(),
|
|
58
|
-
gene: z.object({ name: z.string().optional() }).nullable().optional(),
|
|
59
|
-
interactionAttributes: z.array(z.object({ name: z.string().optional(), value: z.string().optional() })).optional(),
|
|
60
|
-
publications: z.array(z.object({ pmid: z.union([z.string(), z.number()]).optional() })).optional(),
|
|
61
|
-
sources: z.array(z.object({ sourceDbName: z.string().optional() })).optional(),
|
|
62
|
-
});
|
|
63
|
-
const RawNodeSchema = z.object({
|
|
64
|
-
name: z.string(),
|
|
65
|
-
conceptId: z.string().nullable().optional(),
|
|
66
|
-
interactions: z.array(RawInteractionSchema).optional(),
|
|
67
|
-
});
|
|
68
|
-
const DgidbResponseSchema = z.object({
|
|
69
|
-
data: z
|
|
70
|
-
.object({
|
|
71
|
-
genes: z.object({ nodes: z.array(RawNodeSchema).optional() }).optional(),
|
|
72
|
-
drugs: z.object({ nodes: z.array(RawNodeSchema).optional() }).optional(),
|
|
73
|
-
})
|
|
74
|
-
.optional(),
|
|
75
|
-
errors: z.array(z.object({ message: z.string().optional() })).optional(),
|
|
76
|
-
});
|
|
77
|
-
function mapInteraction(raw, perspective, inputSide) {
|
|
78
|
-
const sources = (raw.sources ?? []).map((s) => s.sourceDbName ?? "").filter(Boolean);
|
|
79
|
-
const pmids = (raw.publications ?? []).map((p) => (p.pmid != null ? String(p.pmid) : "")).filter(Boolean);
|
|
80
|
-
const types = (raw.interactionTypes ?? []).map((t) => ({
|
|
81
|
-
type: t.type ?? "",
|
|
82
|
-
...(t.directionality ? { directionality: t.directionality } : {}),
|
|
83
|
-
}));
|
|
84
|
-
const attributes = (raw.interactionAttributes ?? []).slice(0, 20).map((a) => ({
|
|
85
|
-
name: a.name ?? "",
|
|
86
|
-
value: (a.value ?? "").slice(0, 200),
|
|
87
|
-
}));
|
|
88
|
-
const geneName = perspective === "gene" ? (inputSide.geneName ?? "") : (raw.gene?.name ?? "");
|
|
89
|
-
const drugName = perspective === "drug" ? (inputSide.drugName ?? "") : (raw.drug?.name ?? "");
|
|
90
|
-
const drugConceptId = perspective === "drug" ? inputSide.drugConceptId : (raw.drug?.conceptId ?? undefined);
|
|
91
|
-
return {
|
|
92
|
-
geneName,
|
|
93
|
-
drugName,
|
|
94
|
-
...(drugConceptId ? { drugConceptId } : {}),
|
|
95
|
-
interactionTypes: types,
|
|
96
|
-
...(raw.interactionScore != null ? { interactionScore: raw.interactionScore } : {}),
|
|
97
|
-
sourceCount: sources.length,
|
|
98
|
-
sources,
|
|
99
|
-
publicationCount: pmids.length,
|
|
100
|
-
pmids: pmids.slice(0, 10),
|
|
101
|
-
attributes,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
function applyFilters(interactions, filters) {
|
|
105
|
-
let out = interactions;
|
|
106
|
-
if (filters.interactionTypes?.length) {
|
|
107
|
-
const needles = filters.interactionTypes.map((s) => s.toLowerCase());
|
|
108
|
-
out = out.filter((i) => i.interactionTypes.some((t) => needles.some((n) => t.type.toLowerCase().includes(n))));
|
|
109
|
-
}
|
|
110
|
-
if (filters.sources?.length) {
|
|
111
|
-
const needles = filters.sources.map((s) => s.toLowerCase());
|
|
112
|
-
out = out.filter((i) => i.sources.some((s) => needles.some((n) => s.toLowerCase().includes(n))));
|
|
113
|
-
}
|
|
114
|
-
if (filters.minSources > 0) {
|
|
115
|
-
out = out.filter((i) => i.sourceCount >= filters.minSources);
|
|
116
|
-
}
|
|
117
|
-
return out;
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* A factory rather than a bare const (unlike its bio-leaf siblings) because it
|
|
121
|
-
* logs: DGIdb answers GraphQL partial errors alongside usable data, and an
|
|
122
|
-
* operator debugging odd interaction results needs to see them. `ToolContext`
|
|
123
|
-
* carries no deps by design, so a tool that logs is dep-bearing — the same
|
|
124
|
-
* reason `createSearchClinvarTool` takes its key that way.
|
|
125
|
-
*/
|
|
126
|
-
export function createSearchDgidbTool(deps = {}) {
|
|
127
|
-
const logger = (deps.logger ?? createNoopLogger()).named("search_dgidb");
|
|
128
|
-
return defineTool({
|
|
129
|
-
id: "search_dgidb",
|
|
130
|
-
description: "Query the Drug-Gene Interaction Database (DGIdb) for drug-gene interactions. " +
|
|
131
|
-
"DGIdb aggregates 30+ sources (ChEMBL, DrugBank, TTD, PharmGKB, GuideToPharmacology, CIViC, etc.) " +
|
|
132
|
-
"and returns source counts as a confidence signal. " +
|
|
133
|
-
"searchType='gene' returns drugs interacting with input HUGO gene symbols (most common: 'what drugs hit my gene set?'). " +
|
|
134
|
-
"searchType='drug' returns the genes each input drug is known to act on. " +
|
|
135
|
-
"Free, no API key required. Sorted by source count desc.",
|
|
136
|
-
inputSchema: z.object({
|
|
137
|
-
query: z
|
|
138
|
-
.union([z.string(), z.array(z.string()).max(50)])
|
|
139
|
-
.describe("Single identifier or list of up to 50. For searchType='gene': HUGO symbols. For searchType='drug': drug names or DGIdb concept IDs."),
|
|
140
|
-
searchType: z.enum(["gene", "drug"]).default("gene"),
|
|
141
|
-
interactionTypes: z
|
|
142
|
-
.array(z.string())
|
|
143
|
-
.optional()
|
|
144
|
-
.describe("Case-insensitive substring filter on interaction type. e.g. ['inhibitor', 'antagonist']"),
|
|
145
|
-
sources: z.array(z.string()).optional().describe("Restrict to interactions from these source DBs (case-insensitive substring)."),
|
|
146
|
-
minSources: z.number().int().min(1).default(1).describe("Drop interactions supported by fewer than this many sources."),
|
|
147
|
-
limit: z.number().int().min(1).max(200).default(50).describe("Max interactions per input gene/drug after filtering."),
|
|
148
|
-
}),
|
|
149
|
-
execute: async ({ query, searchType = "gene", interactionTypes, sources, minSources = 1, limit = 50 }) => {
|
|
150
|
-
const inputs = typeof query === "string" ? [query] : query;
|
|
151
|
-
if (inputs.length === 0 || inputs.every((s) => !s.trim())) {
|
|
152
|
-
throw new Error("query must contain at least one identifier");
|
|
153
|
-
}
|
|
154
|
-
const isGene = searchType === "gene";
|
|
155
|
-
const variableName = isGene ? "genes" : "drugs";
|
|
156
|
-
const queryString = isGene ? GENE_INTERACTIONS_QUERY : DRUG_INTERACTIONS_QUERY;
|
|
157
|
-
const res = await apiFetchValidated(DGIDB_GRAPHQL_URL, DgidbResponseSchema, {
|
|
158
|
-
method: "POST",
|
|
159
|
-
headers: { "Content-Type": "application/json" },
|
|
160
|
-
body: JSON.stringify({
|
|
161
|
-
query: queryString,
|
|
162
|
-
variables: { [variableName]: inputs },
|
|
163
|
-
}),
|
|
164
|
-
});
|
|
165
|
-
if (res.isErr())
|
|
166
|
-
throw new Error(describeApiError(res.error));
|
|
167
|
-
const body = res.value;
|
|
168
|
-
if (!body || !body.data)
|
|
169
|
-
throw new Error("DGIdb returned no data");
|
|
170
|
-
if (body.errors?.length) {
|
|
171
|
-
logger.warn("dgidb partial errors alongside data", { errors: body.errors.slice(0, 3) });
|
|
172
|
-
}
|
|
173
|
-
const nodes = (isGene ? body.data.genes?.nodes : body.data.drugs?.nodes) ?? [];
|
|
174
|
-
const nodeMap = new Map();
|
|
175
|
-
for (const node of nodes) {
|
|
176
|
-
nodeMap.set(node.name.toLowerCase(), node);
|
|
177
|
-
if (!isGene && node.conceptId) {
|
|
178
|
-
nodeMap.set(node.conceptId.toLowerCase(), node);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
const results = inputs.map((input) => {
|
|
182
|
-
const node = nodeMap.get(input.toLowerCase());
|
|
183
|
-
if (!node) {
|
|
184
|
-
return { input, found: false, interactions: [] };
|
|
185
|
-
}
|
|
186
|
-
const inputSide = isGene
|
|
187
|
-
? { geneName: node.name }
|
|
188
|
-
: {
|
|
189
|
-
drugName: node.name,
|
|
190
|
-
drugConceptId: node.conceptId ?? undefined,
|
|
191
|
-
};
|
|
192
|
-
const mapped = (node.interactions ?? []).map((raw) => mapInteraction(raw, searchType, inputSide));
|
|
193
|
-
const filtered = applyFilters(mapped, {
|
|
194
|
-
interactionTypes,
|
|
195
|
-
sources,
|
|
196
|
-
minSources,
|
|
197
|
-
});
|
|
198
|
-
const sorted = filtered.sort((a, b) => {
|
|
199
|
-
if (b.sourceCount !== a.sourceCount) {
|
|
200
|
-
return b.sourceCount - a.sourceCount;
|
|
201
|
-
}
|
|
202
|
-
return (b.interactionScore ?? 0) - (a.interactionScore ?? 0);
|
|
203
|
-
});
|
|
204
|
-
return {
|
|
205
|
-
input,
|
|
206
|
-
found: true,
|
|
207
|
-
interactions: sorted.slice(0, limit),
|
|
208
|
-
};
|
|
209
|
-
});
|
|
210
|
-
return ok({ results });
|
|
211
|
-
},
|
|
212
|
-
});
|
|
213
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* searchDisgenet — search DisGeNET for gene-disease associations (GDAs).
|
|
3
|
-
*
|
|
4
|
-
* Requires `DISGENET_API_KEY`. Without the key, `getDisgenetHeaders` throws
|
|
5
|
-
* on first call; the harness surfaces that as a tool `is_error` envelope.
|
|
6
|
-
*/
|
|
7
|
-
export declare function createSearchDisgenetTool(deps: {
|
|
8
|
-
apiKey: string;
|
|
9
|
-
}): import("../define-tool.js").Tool<{
|
|
10
|
-
query: string;
|
|
11
|
-
searchType: "gene" | "disease";
|
|
12
|
-
minScore: number;
|
|
13
|
-
source: "ALL" | "CURATED" | "ANIMAL_MODELS" | "BEFREE";
|
|
14
|
-
limit: number;
|
|
15
|
-
}, {
|
|
16
|
-
associations: {
|
|
17
|
-
geneSymbol: string;
|
|
18
|
-
geneName: string;
|
|
19
|
-
geneId: number;
|
|
20
|
-
diseaseName: string;
|
|
21
|
-
diseaseId: string;
|
|
22
|
-
diseaseType: string;
|
|
23
|
-
score: number;
|
|
24
|
-
evidenceIndex: number;
|
|
25
|
-
yearInitial: number | null;
|
|
26
|
-
yearFinal: number | null;
|
|
27
|
-
nPmids: number;
|
|
28
|
-
source: string;
|
|
29
|
-
}[];
|
|
30
|
-
}>;
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* searchDisgenet — search DisGeNET for gene-disease associations (GDAs).
|
|
3
|
-
*
|
|
4
|
-
* Requires `DISGENET_API_KEY`. Without the key, `getDisgenetHeaders` throws
|
|
5
|
-
* on first call; the harness surfaces that as a tool `is_error` envelope.
|
|
6
|
-
*/
|
|
7
|
-
import { ok } from "neverthrow";
|
|
8
|
-
import { z } from "zod";
|
|
9
|
-
import { defineTool } from "../define-tool.js";
|
|
10
|
-
import { apiFetchValidated, describeApiError } from "../lib/api-utils.js";
|
|
11
|
-
import { DISGENET_BASE, getDisgenetHeaders } from "../lib/disgenet-config.js";
|
|
12
|
-
// A single schema that both validates and normalizes one DisGeNET GDA record:
|
|
13
|
-
// the `.object(...)` half is the snake_case wire shape (every field optional —
|
|
14
|
-
// the API omits absent values), the `.transform(...)` half maps it to the
|
|
15
|
-
// camelCase `Gda` we return. Parsing IS the validation (`apiFetchValidated`
|
|
16
|
-
// runs it over the JSON), and because the transform rides on the schema,
|
|
17
|
-
// `z.infer` below is the OUTPUT type — no separate raw interface or mapper.
|
|
18
|
-
const GdaSchema = z
|
|
19
|
-
.object({
|
|
20
|
-
gene_symbol: z.string().nullable().optional(),
|
|
21
|
-
gene_name: z.string().nullable().optional(),
|
|
22
|
-
geneid: z.number().nullable().optional(),
|
|
23
|
-
disease_name: z.string().nullable().optional(),
|
|
24
|
-
diseaseid: z.string().nullable().optional(),
|
|
25
|
-
disease_type: z.string().nullable().optional(),
|
|
26
|
-
score: z.number().nullable().optional(),
|
|
27
|
-
ei: z.number().nullable().optional(),
|
|
28
|
-
year_initial: z.number().nullable().optional(),
|
|
29
|
-
year_final: z.number().nullable().optional(),
|
|
30
|
-
pmid_count: z.number().nullable().optional(),
|
|
31
|
-
source: z.string().nullable().optional(),
|
|
32
|
-
})
|
|
33
|
-
.transform((gda) => ({
|
|
34
|
-
geneSymbol: gda.gene_symbol ?? "",
|
|
35
|
-
geneName: gda.gene_name ?? "",
|
|
36
|
-
geneId: gda.geneid ?? 0,
|
|
37
|
-
diseaseName: gda.disease_name ?? "",
|
|
38
|
-
diseaseId: gda.diseaseid ?? "",
|
|
39
|
-
diseaseType: gda.disease_type ?? "",
|
|
40
|
-
score: gda.score ?? 0,
|
|
41
|
-
evidenceIndex: gda.ei ?? 0,
|
|
42
|
-
yearInitial: gda.year_initial ?? null,
|
|
43
|
-
yearFinal: gda.year_final ?? null,
|
|
44
|
-
nPmids: gda.pmid_count ?? 0,
|
|
45
|
-
source: gda.source ?? "",
|
|
46
|
-
}));
|
|
47
|
-
const GdaListSchema = z.array(GdaSchema);
|
|
48
|
-
export function createSearchDisgenetTool(deps) {
|
|
49
|
-
return defineTool({
|
|
50
|
-
id: "search_disgenet",
|
|
51
|
-
description: "Search DisGeNET for gene-disease associations (GDAs). Returns association scores, " +
|
|
52
|
-
"evidence counts, and disease classifications. Requires DISGENET_API_KEY. " +
|
|
53
|
-
"Use for target validation — high-scoring GDAs provide genetic support for a " +
|
|
54
|
-
"therapeutic target's disease relevance.",
|
|
55
|
-
inputSchema: z.object({
|
|
56
|
-
query: z.string().describe("Gene symbol (e.g. TP53), Entrez gene ID (e.g. 7157), or disease name/UMLS CUI (e.g. C0006142 for breast cancer)"),
|
|
57
|
-
searchType: z.enum(["gene", "disease"]).describe("'gene' to find diseases for a gene, 'disease' to find genes for a disease"),
|
|
58
|
-
minScore: z.number().min(0).max(1).default(0.1).describe("Minimum GDA score (0–1). Higher = stronger evidence"),
|
|
59
|
-
source: z.enum(["ALL", "CURATED", "ANIMAL_MODELS", "BEFREE"]).default("ALL").describe("Evidence source filter"),
|
|
60
|
-
limit: z.number().int().min(1).max(100).default(25).describe("Max results to return"),
|
|
61
|
-
}),
|
|
62
|
-
execute: async ({ query, searchType, minScore = 0.1, source = "ALL", limit = 25 }) => {
|
|
63
|
-
const headers = getDisgenetHeaders(deps.apiKey);
|
|
64
|
-
let url;
|
|
65
|
-
if (searchType === "gene") {
|
|
66
|
-
const isNumeric = /^\d+$/.test(query);
|
|
67
|
-
const param = isNumeric ? `gene/${query}` : `gene/${encodeURIComponent(query)}`;
|
|
68
|
-
url = `${DISGENET_BASE}/gda/${param}?min_score=${minScore}&limit=${limit}`;
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
url = `${DISGENET_BASE}/gda/disease/${encodeURIComponent(query)}?min_score=${minScore}&limit=${limit}`;
|
|
72
|
-
}
|
|
73
|
-
if (source !== "ALL") {
|
|
74
|
-
url += `&source=${source}`;
|
|
75
|
-
}
|
|
76
|
-
const res = await apiFetchValidated(url, GdaListSchema, { headers });
|
|
77
|
-
if (res.isErr())
|
|
78
|
-
throw new Error(describeApiError(res.error));
|
|
79
|
-
// Already validated + normalized to camelCase by GdaSchema's transform.
|
|
80
|
-
const associations = res.value;
|
|
81
|
-
return ok({ associations });
|
|
82
|
-
},
|
|
83
|
-
});
|
|
84
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* searchDrugbank — search DrugBank for drug records and target-driven lookups.
|
|
3
|
-
*
|
|
4
|
-
* Requires `DRUGBANK_API_KEY`. Without the key, `getDrugbankHeaders` throws
|
|
5
|
-
* on first call; the harness surfaces that as a tool `is_error` envelope.
|
|
6
|
-
*/
|
|
7
|
-
export declare function createSearchDrugbankTool(deps: {
|
|
8
|
-
apiKey: string;
|
|
9
|
-
}): import("../define-tool.js").Tool<{
|
|
10
|
-
query: string;
|
|
11
|
-
searchType: "target" | "drug";
|
|
12
|
-
limit: number;
|
|
13
|
-
}, {
|
|
14
|
-
drugs: {
|
|
15
|
-
drugbankId: string;
|
|
16
|
-
name: string;
|
|
17
|
-
description: string;
|
|
18
|
-
type: string;
|
|
19
|
-
groups: string[];
|
|
20
|
-
categories: string[];
|
|
21
|
-
indication: string;
|
|
22
|
-
pharmacodynamics: string;
|
|
23
|
-
mechanismOfAction: string;
|
|
24
|
-
toxicity: string;
|
|
25
|
-
halfLife: string;
|
|
26
|
-
targets: {
|
|
27
|
-
name: string;
|
|
28
|
-
geneSymbol: string;
|
|
29
|
-
actions: string[];
|
|
30
|
-
knownAction: string;
|
|
31
|
-
}[];
|
|
32
|
-
interactions: {
|
|
33
|
-
drugbankId: string;
|
|
34
|
-
name: string;
|
|
35
|
-
description: string;
|
|
36
|
-
}[];
|
|
37
|
-
}[];
|
|
38
|
-
}>;
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* searchDrugbank — search DrugBank for drug records and target-driven lookups.
|
|
3
|
-
*
|
|
4
|
-
* Requires `DRUGBANK_API_KEY`. Without the key, `getDrugbankHeaders` throws
|
|
5
|
-
* on first call; the harness surfaces that as a tool `is_error` envelope.
|
|
6
|
-
*/
|
|
7
|
-
import { ok } from "neverthrow";
|
|
8
|
-
import { z } from "zod";
|
|
9
|
-
import { defineTool } from "../define-tool.js";
|
|
10
|
-
import { apiFetchValidated, describeApiError } from "../lib/api-utils.js";
|
|
11
|
-
import { DRUGBANK_BASE, getDrugbankHeaders } from "../lib/drugbank-config.js";
|
|
12
|
-
// A single schema that both validates and normalizes one DrugBank record.
|
|
13
|
-
// The `.object(...)` half is the snake_case wire shape (every field optional —
|
|
14
|
-
// the API omits absent values); the `.transform(...)` half maps it into the
|
|
15
|
-
// camelCase result we return. Parsing IS the validation: `apiFetchValidated`
|
|
16
|
-
// runs this schema over the JSON, so a payload whose field TYPES drift (an
|
|
17
|
-
// object where the API used to send an array, a number for a string) is
|
|
18
|
-
// rejected as `invalid_response` instead of being silently mis-mapped. Because
|
|
19
|
-
// the schema carries the transform, `z.infer` below is the OUTPUT (camelCase)
|
|
20
|
-
// type callers receive — there is no separate raw interface or mapper to keep
|
|
21
|
-
// in sync.
|
|
22
|
-
const DrugResultSchema = z
|
|
23
|
-
.object({
|
|
24
|
-
drugbank_id: z.string().optional(),
|
|
25
|
-
name: z.string().optional(),
|
|
26
|
-
description: z.string().optional(),
|
|
27
|
-
type: z.string().optional(),
|
|
28
|
-
groups: z.array(z.string()).optional(),
|
|
29
|
-
categories: z.array(z.object({ category: z.string().optional() })).optional(),
|
|
30
|
-
indication: z.string().optional(),
|
|
31
|
-
pharmacodynamics: z.string().optional(),
|
|
32
|
-
mechanism_of_action: z.string().optional(),
|
|
33
|
-
toxicity: z.string().optional(),
|
|
34
|
-
half_life: z.string().optional(),
|
|
35
|
-
targets: z
|
|
36
|
-
.array(z.object({
|
|
37
|
-
name: z.string().optional(),
|
|
38
|
-
gene_name: z.string().optional(),
|
|
39
|
-
actions: z.array(z.string()).optional(),
|
|
40
|
-
known_action: z.string().optional(),
|
|
41
|
-
}))
|
|
42
|
-
.optional(),
|
|
43
|
-
drug_interactions: z
|
|
44
|
-
.array(z.object({
|
|
45
|
-
drugbank_id: z.string().optional(),
|
|
46
|
-
name: z.string().optional(),
|
|
47
|
-
description: z.string().optional(),
|
|
48
|
-
}))
|
|
49
|
-
.optional(),
|
|
50
|
-
})
|
|
51
|
-
.transform((raw) => ({
|
|
52
|
-
drugbankId: raw.drugbank_id ?? "",
|
|
53
|
-
name: raw.name ?? "",
|
|
54
|
-
description: (raw.description ?? "").slice(0, 500),
|
|
55
|
-
type: raw.type ?? "",
|
|
56
|
-
groups: raw.groups ?? [],
|
|
57
|
-
categories: (raw.categories ?? []).map((c) => c.category ?? "").filter(Boolean),
|
|
58
|
-
indication: (raw.indication ?? "").slice(0, 500),
|
|
59
|
-
pharmacodynamics: (raw.pharmacodynamics ?? "").slice(0, 500),
|
|
60
|
-
mechanismOfAction: (raw.mechanism_of_action ?? "").slice(0, 500),
|
|
61
|
-
toxicity: (raw.toxicity ?? "").slice(0, 500),
|
|
62
|
-
halfLife: raw.half_life ?? "",
|
|
63
|
-
targets: (raw.targets ?? []).slice(0, 20).map((t) => ({
|
|
64
|
-
name: t.name ?? "",
|
|
65
|
-
geneSymbol: t.gene_name ?? "",
|
|
66
|
-
actions: t.actions ?? [],
|
|
67
|
-
knownAction: t.known_action ?? "",
|
|
68
|
-
})),
|
|
69
|
-
interactions: (raw.drug_interactions ?? []).slice(0, 20).map((i) => ({
|
|
70
|
-
drugbankId: i.drugbank_id ?? "",
|
|
71
|
-
name: i.name ?? "",
|
|
72
|
-
description: (i.description ?? "").slice(0, 200),
|
|
73
|
-
})),
|
|
74
|
-
}));
|
|
75
|
-
// The /drugs endpoint returns a single object (by-id lookup) or an array
|
|
76
|
-
// (query/target lookups); accept either, transforming each element.
|
|
77
|
-
const DrugResponseSchema = z.union([DrugResultSchema, z.array(DrugResultSchema)]);
|
|
78
|
-
export function createSearchDrugbankTool(deps) {
|
|
79
|
-
return defineTool({
|
|
80
|
-
id: "search_drugbank",
|
|
81
|
-
description: "Search DrugBank for drug information including targets, interactions, indications, " +
|
|
82
|
-
"pharmacodynamics, and toxicity. Requires DRUGBANK_API_KEY. Use for drug repurposing " +
|
|
83
|
-
"analysis, drug-drug interaction assessment, and connecting omics targets to existing therapeutics.",
|
|
84
|
-
inputSchema: z.object({
|
|
85
|
-
query: z.string().describe("Drug name (e.g. imatinib), DrugBank ID (e.g. DB00619), or target gene symbol for reverse lookup"),
|
|
86
|
-
searchType: z.enum(["drug", "target"]).default("drug").describe("'drug' to search by drug name/ID, 'target' to find drugs for a gene target"),
|
|
87
|
-
limit: z.number().int().min(1).max(50).default(10).describe("Max results to return"),
|
|
88
|
-
}),
|
|
89
|
-
execute: async ({ query, searchType = "drug", limit = 10 }) => {
|
|
90
|
-
const headers = getDrugbankHeaders(deps.apiKey);
|
|
91
|
-
let url;
|
|
92
|
-
if (searchType === "target") {
|
|
93
|
-
url = `${DRUGBANK_BASE}/drugs?target=${encodeURIComponent(query)}&limit=${limit}`;
|
|
94
|
-
}
|
|
95
|
-
else if (query.startsWith("DB")) {
|
|
96
|
-
url = `${DRUGBANK_BASE}/drugs/${query}`;
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
url = `${DRUGBANK_BASE}/drugs?q=${encodeURIComponent(query)}&limit=${limit}`;
|
|
100
|
-
}
|
|
101
|
-
const res = await apiFetchValidated(url, DrugResponseSchema, { headers });
|
|
102
|
-
if (res.isErr())
|
|
103
|
-
throw new Error(describeApiError(res.error));
|
|
104
|
-
// Already validated + normalized by DrugResultSchema's transform.
|
|
105
|
-
const drugs = Array.isArray(res.value) ? res.value : [res.value];
|
|
106
|
-
return ok({ drugs: drugs.slice(0, limit) });
|
|
107
|
-
},
|
|
108
|
-
});
|
|
109
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* searchGwasCatalog — search NHGRI-EBI GWAS Catalog for SNP-trait associations.
|
|
3
|
-
*
|
|
4
|
-
* Public REST API, no key required. Three search modes:
|
|
5
|
-
* - 'gene' — gene-trait associations (via SNP-by-gene lookup, then assocs)
|
|
6
|
-
* - 'trait' — trait-gene associations (via EFO trait search, then assocs)
|
|
7
|
-
* - 'variant' — direct rsID lookup
|
|
8
|
-
*/
|
|
9
|
-
interface GwasAssociation {
|
|
10
|
-
rsId: string;
|
|
11
|
-
pValue: number;
|
|
12
|
-
pValueMantissa: number;
|
|
13
|
-
pValueExponent: number;
|
|
14
|
-
riskAllele: string;
|
|
15
|
-
riskFrequency: string;
|
|
16
|
-
orBeta: number | null;
|
|
17
|
-
ci: string;
|
|
18
|
-
trait: string;
|
|
19
|
-
mappedGenes: string[];
|
|
20
|
-
studyAccession: string;
|
|
21
|
-
pubmedId: string;
|
|
22
|
-
sampleSize: string;
|
|
23
|
-
}
|
|
24
|
-
export declare const searchGwasCatalogTool: import("../define-tool.js").Tool<{
|
|
25
|
-
query: string;
|
|
26
|
-
searchType: "gene" | "variant" | "trait";
|
|
27
|
-
pValueThreshold: number;
|
|
28
|
-
limit: number;
|
|
29
|
-
}, {
|
|
30
|
-
totalFound: number;
|
|
31
|
-
associations: GwasAssociation[];
|
|
32
|
-
}>;
|
|
33
|
-
export {};
|