@inflexa-ai/harness 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/conversation-agent.d.ts +9 -15
- package/dist/agents/conversation-agent.js +32 -31
- 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/index.js +0 -3
- 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.d.ts +2 -2
- package/dist/agents/sandbox/shared.js +8 -14
- 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/config/resource-limits.d.ts +0 -7
- package/dist/config/resource-limits.js +0 -1
- package/dist/contracts/target-dossier.d.ts +10 -10
- package/dist/execution/dbos-run-launcher.js +1 -26
- package/dist/execution/report-runner.js +1 -1
- package/dist/execution/run-authorizer.d.ts +1 -1
- package/dist/execution/run-authorizer.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-launcher.d.ts +2 -18
- package/dist/execution/run-launcher.js +2 -4
- package/dist/execution/run-synthesis.js +1 -1
- package/dist/index.d.ts +7 -4
- package/dist/index.js +23 -6
- 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 +51 -5
- package/dist/loop/run-to-terminal.d.ts +0 -2
- package/dist/loop/run-to-terminal.js +8 -1
- 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/reconstruct-cards.d.ts +1 -1
- package/dist/memory/reconstruct-cards.js +7 -5
- package/dist/memory/thread-history.js +72 -8
- package/dist/prompts/conversation.d.ts +1 -1
- package/dist/prompts/conversation.js +41 -15
- package/dist/prompts/literature-reviewer.d.ts +1 -1
- package/dist/prompts/literature-reviewer.js +16 -14
- package/dist/prompts/planner.d.ts +3 -0
- package/dist/prompts/planner.js +16 -21
- 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/sandbox-standards.d.ts +4 -4
- package/dist/prompts/sandbox-standards.js +5 -5
- 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/assemble.d.ts +5 -10
- package/dist/runtime/assemble.js +0 -4
- package/dist/runtime/boot.d.ts +2 -2
- package/dist/runtime/boot.js +1 -1
- package/dist/runtime/dbos.d.ts +4 -5
- package/dist/runtime/dbos.js +6 -7
- package/dist/runtime/pools.d.ts +15 -1
- package/dist/runtime/pools.js +15 -1
- package/dist/sandbox/mount-plan.d.ts +1 -1
- package/dist/sandbox/sandbox-error.d.ts +1 -1
- package/dist/sandbox/sandbox-error.js +1 -1
- package/dist/sandbox/sandbox-step-translate.d.ts +15 -2
- package/dist/sandbox/sandbox-step-translate.js +17 -2
- package/dist/sandbox/types.d.ts +1 -1
- package/dist/schemas/validate-plan.d.ts +1 -1
- 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 +6 -6
- package/dist/state/index.js +4 -4
- package/dist/state/init.js +37 -2
- package/dist/state/plans.js +1 -1
- package/dist/state/runs.d.ts +49 -0
- package/dist/state/runs.js +150 -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/__fixtures__/tool-context.js +1 -0
- package/dist/tools/ad-hoc-router.d.ts +31 -0
- package/dist/tools/ad-hoc-router.js +132 -0
- package/dist/tools/analysis-invocation.d.ts +4 -0
- package/dist/tools/analysis-invocation.js +17 -0
- 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/define-tool.d.ts +6 -2
- package/dist/tools/define-tool.js +1 -1
- package/dist/tools/execute-analysis.d.ts +44 -0
- package/dist/tools/execute-analysis.js +252 -0
- package/dist/tools/iterate-report.js +7 -6
- 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 +116 -70
- 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 +68 -5
- package/dist/workflows/execute-analysis.js +76 -7
- 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/agents/sandbox/ephemeral-executor.d.ts +0 -9
- package/dist/agents/sandbox/ephemeral-executor.js +0 -20
- package/dist/execution/ephemeral-runner.d.ts +0 -98
- package/dist/execution/ephemeral-runner.js +0 -189
- package/dist/prompts/sandbox/ephemeral-executor.d.ts +0 -1
- package/dist/prompts/sandbox/ephemeral-executor.js +0 -66
- 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
- package/dist/tools/execute-plan.d.ts +0 -64
- package/dist/tools/execute-plan.js +0 -211
- package/dist/tools/run-ephemeral.d.ts +0 -33
- package/dist/tools/run-ephemeral.js +0 -65
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* genePreclinicalProfile — the in-vivo preclinical picture of one gene: where
|
|
3
|
+
* it is expressed at baseline (Bgee) and what removing it does to a mouse
|
|
4
|
+
* (IMPC), behind one `include`.
|
|
5
|
+
*
|
|
6
|
+
* The two halves share an input exactly — ONE human gene symbol, with the
|
|
7
|
+
* per-species orthologs resolved internally — and answer one question between
|
|
8
|
+
* them: is this target expressed where it needs to act, and is a model organism
|
|
9
|
+
* a fair surrogate for it. They were two tools that were called as a pair.
|
|
10
|
+
*
|
|
11
|
+
* Neither source caps its own output: Bgee returns every annotated tissue per
|
|
12
|
+
* species and IMPC every significant phenotype term, so both halves are trimmed
|
|
13
|
+
* here rather than in the clients — the target-assessment collectors read the
|
|
14
|
+
* same clients and legitimately want full fidelity for the dossier.
|
|
15
|
+
*/
|
|
16
|
+
import { type SupportedSpecies, type TissueRow } from "../lib/bgee-client.js";
|
|
17
|
+
import { type MpTerm, type ViabilityCall, type ViabilityCategory } from "../lib/impc-client.js";
|
|
18
|
+
interface BoundedSpeciesEntry {
|
|
19
|
+
species: SupportedSpecies;
|
|
20
|
+
taxonId: number;
|
|
21
|
+
ensemblId: string;
|
|
22
|
+
tissues: TissueRow[];
|
|
23
|
+
/** Tissues Bgee annotated for this species, before `minRank` and `tissueLimit`. */
|
|
24
|
+
tissueCount: number;
|
|
25
|
+
/** Tissues that passed `minRank`, before `tissueLimit`. */
|
|
26
|
+
tissuesAboveMinRank: number;
|
|
27
|
+
}
|
|
28
|
+
interface ExpressionHalf {
|
|
29
|
+
humanEnsemblId: string | null;
|
|
30
|
+
bySpecies: BoundedSpeciesEntry[];
|
|
31
|
+
notFound: string[];
|
|
32
|
+
}
|
|
33
|
+
interface KnockoutHalf {
|
|
34
|
+
mouseMarkerSymbol: string | null;
|
|
35
|
+
mgiAccessionId: string | null;
|
|
36
|
+
viability: ViabilityCategory;
|
|
37
|
+
viabilityCalls: ViabilityCall[];
|
|
38
|
+
mpTerms: MpTerm[];
|
|
39
|
+
organSystems: string[];
|
|
40
|
+
sexDimorphic: boolean;
|
|
41
|
+
phenotypeCount: number;
|
|
42
|
+
/** True when `phenotypeLimit` trimmed `mpTerms`; `phenotypeCount` is the real total. */
|
|
43
|
+
phenotypesTrimmed: boolean;
|
|
44
|
+
}
|
|
45
|
+
type PreclinicalOutput = {
|
|
46
|
+
geneSymbol: string;
|
|
47
|
+
expression?: ExpressionHalf;
|
|
48
|
+
knockout?: KnockoutHalf;
|
|
49
|
+
};
|
|
50
|
+
export declare const genePreclinicalProfileTool: import("../define-tool.js").Tool<{
|
|
51
|
+
geneSymbol: string;
|
|
52
|
+
include?: ("expression" | "knockout")[] | undefined;
|
|
53
|
+
species?: ("homo_sapiens" | "mus_musculus" | "rattus_norvegicus" | "canis_lupus_familiaris" | "macaca_mulatta")[] | undefined;
|
|
54
|
+
minRank?: "high" | "medium" | "low" | "absent" | undefined;
|
|
55
|
+
tissueLimit?: number | undefined;
|
|
56
|
+
phenotypeLimit?: number | undefined;
|
|
57
|
+
}, PreclinicalOutput>;
|
|
58
|
+
export {};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* genePreclinicalProfile — the in-vivo preclinical picture of one gene: where
|
|
3
|
+
* it is expressed at baseline (Bgee) and what removing it does to a mouse
|
|
4
|
+
* (IMPC), behind one `include`.
|
|
5
|
+
*
|
|
6
|
+
* The two halves share an input exactly — ONE human gene symbol, with the
|
|
7
|
+
* per-species orthologs resolved internally — and answer one question between
|
|
8
|
+
* them: is this target expressed where it needs to act, and is a model organism
|
|
9
|
+
* a fair surrogate for it. They were two tools that were called as a pair.
|
|
10
|
+
*
|
|
11
|
+
* Neither source caps its own output: Bgee returns every annotated tissue per
|
|
12
|
+
* species and IMPC every significant phenotype term, so both halves are trimmed
|
|
13
|
+
* here rather than in the clients — the target-assessment collectors read the
|
|
14
|
+
* same clients and legitimately want full fidelity for the dossier.
|
|
15
|
+
*/
|
|
16
|
+
import { ok } from "neverthrow";
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
import { defineTool } from "../define-tool.js";
|
|
19
|
+
import { SUPPORTED_SPECIES, getMultiSpeciesExpression } from "../lib/bgee-client.js";
|
|
20
|
+
import { getKoPhenotypeProfile } from "../lib/impc-client.js";
|
|
21
|
+
/** Ordered weakest → strongest so `minRank` can be a numeric floor. */
|
|
22
|
+
const RANK_ORDER = { absent: 0, low: 1, medium: 2, high: 3 };
|
|
23
|
+
/** Human + mouse: the pairing that answers "is the mouse a fair surrogate?". */
|
|
24
|
+
const DEFAULT_SPECIES = ["homo_sapiens", "mus_musculus"];
|
|
25
|
+
const DEFAULTS = { tissueLimit: 15, phenotypeLimit: 20, minRank: "low" };
|
|
26
|
+
const inputSchema = z.object({
|
|
27
|
+
geneSymbol: z
|
|
28
|
+
.string()
|
|
29
|
+
.min(1)
|
|
30
|
+
.describe("HUMAN gene symbol, e.g. 'BRCA1' — one gene per call. Ensembl IDs, mouse symbols and MGI IDs are NOT accepted; the ENSG and the per-species " +
|
|
31
|
+
"orthologs are resolved for you."),
|
|
32
|
+
include: z
|
|
33
|
+
.array(z.enum(["expression", "knockout"]))
|
|
34
|
+
.min(1)
|
|
35
|
+
.optional()
|
|
36
|
+
.describe("Default BOTH — two sides of one judgement, one request each. 'expression': Bgee baseline (healthy, untreated) expression per species and " +
|
|
37
|
+
"tissue. 'knockout': IMPC mouse loss-of-function phenotype."),
|
|
38
|
+
species: z
|
|
39
|
+
.array(z.enum(SUPPORTED_SPECIES))
|
|
40
|
+
.min(1)
|
|
41
|
+
.optional()
|
|
42
|
+
.describe("'expression' only. Default ['homo_sapiens','mus_musculus'] — the surrogate question. Add rat/dog/macaque when the cross-species comparison " +
|
|
43
|
+
"is the point; dog and macaque coverage is thin, so expect empty entries."),
|
|
44
|
+
minRank: z
|
|
45
|
+
.enum(["absent", "low", "medium", "high"])
|
|
46
|
+
.optional()
|
|
47
|
+
.describe("'expression' only. Drop tissues below this bucketed rank; default 'low' (drops only not-expressed). Use 'medium'/'high' for just the tissues " +
|
|
48
|
+
"where the gene is abundant — the usual tissue-of-action question."),
|
|
49
|
+
tissueLimit: z
|
|
50
|
+
.number()
|
|
51
|
+
.int()
|
|
52
|
+
.min(1)
|
|
53
|
+
.max(100)
|
|
54
|
+
.optional()
|
|
55
|
+
.describe(`'expression' only. Max tissues per species after minRank, highest expression first (default ${DEFAULTS.tissueLimit}, max 100). ` +
|
|
56
|
+
"`tissueCount`/`tissuesAboveMinRank` give the pre-trim counts."),
|
|
57
|
+
phenotypeLimit: z
|
|
58
|
+
.number()
|
|
59
|
+
.int()
|
|
60
|
+
.min(1)
|
|
61
|
+
.max(200)
|
|
62
|
+
.optional()
|
|
63
|
+
.describe(`'knockout' only. Max MP phenotype terms, best p-value first (default ${DEFAULTS.phenotypeLimit}, max 200). \`phenotypeCount\` gives the ` +
|
|
64
|
+
"true total; `organSystems` is never trimmed, so the top-line picture survives."),
|
|
65
|
+
});
|
|
66
|
+
function boundExpression(raw, minRank, tissueLimit) {
|
|
67
|
+
const floor = RANK_ORDER[minRank];
|
|
68
|
+
return {
|
|
69
|
+
humanEnsemblId: raw.humanEnsemblId,
|
|
70
|
+
bySpecies: raw.bySpecies.map((entry) => {
|
|
71
|
+
const aboveFloor = entry.tissues.filter((t) => RANK_ORDER[t.rank] >= floor);
|
|
72
|
+
const ranked = [...aboveFloor].sort((a, b) => (b.expressionScore ?? 0) - (a.expressionScore ?? 0));
|
|
73
|
+
return {
|
|
74
|
+
species: entry.species,
|
|
75
|
+
taxonId: entry.taxonId,
|
|
76
|
+
ensemblId: entry.ensemblId,
|
|
77
|
+
tissues: ranked.slice(0, tissueLimit),
|
|
78
|
+
tissueCount: entry.tissues.length,
|
|
79
|
+
tissuesAboveMinRank: aboveFloor.length,
|
|
80
|
+
};
|
|
81
|
+
}),
|
|
82
|
+
notFound: raw.notFound,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function boundKnockout(raw, phenotypeLimit) {
|
|
86
|
+
return {
|
|
87
|
+
mouseMarkerSymbol: raw.mouseMarkerSymbol,
|
|
88
|
+
mgiAccessionId: raw.mgiAccessionId,
|
|
89
|
+
viability: raw.viability,
|
|
90
|
+
viabilityCalls: raw.viabilityCalls,
|
|
91
|
+
mpTerms: raw.mpTerms.slice(0, phenotypeLimit),
|
|
92
|
+
organSystems: raw.organSystems,
|
|
93
|
+
sexDimorphic: raw.sexDimorphic,
|
|
94
|
+
phenotypeCount: raw.phenotypeCount,
|
|
95
|
+
phenotypesTrimmed: raw.mpTerms.length > phenotypeLimit,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export const genePreclinicalProfileTool = defineTool({
|
|
99
|
+
id: "gene_preclinical_profile",
|
|
100
|
+
description: "The in-vivo preclinical profile of ONE human gene — baseline expression (Bgee) and mouse-knockout phenotype (IMPC) in one call. Answers " +
|
|
101
|
+
"tissue-of-action ('where does this target act?'), model-organism suitability ('is the mouse a fair surrogate?') and essentiality ('what happens " +
|
|
102
|
+
"when this gene is lost?').\n" +
|
|
103
|
+
"Expression is BASELINE (healthy, untreated) only — differential expression between conditions is the analysis pipeline's job, never this tool.\n" +
|
|
104
|
+
"Output is trimmed by default (see `minRank`, `tissueLimit`, `phenotypeLimit`); the accompanying counts distinguish a trimmed list from a sparse one.\n" +
|
|
105
|
+
"SPARSE OUTPUT IS VALID NO-DATA, not an error — do not retry. A null humanEnsemblId means the symbol did not resolve; a null mouseMarkerSymbol " +
|
|
106
|
+
"with no phenotype terms means the gene has not been IMPC-phenotyped, which is common.",
|
|
107
|
+
inputSchema,
|
|
108
|
+
execute: async ({ geneSymbol, include, species, minRank, tissueLimit, phenotypeLimit }) => {
|
|
109
|
+
const halves = include ?? ["expression", "knockout"];
|
|
110
|
+
const wantExpression = halves.includes("expression");
|
|
111
|
+
const wantKnockout = halves.includes("knockout");
|
|
112
|
+
const [expressionRaw, knockoutRaw] = await Promise.all([
|
|
113
|
+
wantExpression ? getMultiSpeciesExpression(geneSymbol, [...(species ?? DEFAULT_SPECIES)]) : Promise.resolve(null),
|
|
114
|
+
wantKnockout ? getKoPhenotypeProfile(geneSymbol) : Promise.resolve(null),
|
|
115
|
+
]);
|
|
116
|
+
const output = { geneSymbol };
|
|
117
|
+
if (expressionRaw) {
|
|
118
|
+
output.expression = boundExpression(expressionRaw, minRank ?? DEFAULTS.minRank, tissueLimit ?? DEFAULTS.tissueLimit);
|
|
119
|
+
}
|
|
120
|
+
if (knockoutRaw) {
|
|
121
|
+
output.knockout = boundKnockout(knockoutRaw, phenotypeLimit ?? DEFAULTS.phenotypeLimit);
|
|
122
|
+
}
|
|
123
|
+
return ok(output);
|
|
124
|
+
},
|
|
125
|
+
});
|
|
@@ -1,29 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Bio barrel — pure external-API bioinformatics & cheminformatics lookups.
|
|
3
3
|
*
|
|
4
|
-
* Leaf tools with no harness dependencies: each wraps
|
|
5
|
-
* (Ensembl, ChEMBL, PubChem, Open Targets, PubMed, EPA CompTox, …)
|
|
6
|
-
* `defineTool`. Grouped here so the conversation agent imports one
|
|
7
|
-
* rather than
|
|
4
|
+
* Leaf tools with no harness dependencies: each wraps one or more external data
|
|
5
|
+
* sources (Ensembl, ChEMBL, PubChem, Open Targets, PubMed, EPA CompTox, …)
|
|
6
|
+
* behind a `defineTool`. Grouped here so the conversation agent imports one
|
|
7
|
+
* barrel rather than a file per tool.
|
|
8
|
+
*
|
|
9
|
+
* The boundary is the QUESTION, not the database. Where several corpora answer
|
|
10
|
+
* the same question, the corpus is a `sources` parameter and the tool fans out
|
|
11
|
+
* (`gene_disease_evidence`, `drug_gene_interactions`, `target_safety`,
|
|
12
|
+
* `lookup_annotation`, `gene_preclinical_profile`); where one database answers
|
|
13
|
+
* several questions, the endpoint is an `action` parameter (`chembl`,
|
|
14
|
+
* `pubchem`, `pubmed`, `comptox`, `opentargets`, `search_interactions`).
|
|
15
|
+
*
|
|
16
|
+
* Every tool returns a BOUNDED result by default. A default call is sized to be
|
|
17
|
+
* read, not to be complete: limits are small, verbose fields are opt-in, and
|
|
18
|
+
* each result carries the true totals (`totalFound`, `totalInteractions`,
|
|
19
|
+
* `tissueCount`, `outline`, …) so a caller can tell a trimmed answer from a
|
|
20
|
+
* sparse one and widen deliberately.
|
|
8
21
|
*/
|
|
9
22
|
export * from "./search-gene.js";
|
|
10
|
-
export * from "./
|
|
11
|
-
export * from "./lookup-go-term.js";
|
|
23
|
+
export * from "./lookup-annotation.js";
|
|
12
24
|
export * from "./search-interactions.js";
|
|
13
25
|
export * from "./pubmed.js";
|
|
14
26
|
export * from "./chembl.js";
|
|
15
27
|
export * from "./pubchem.js";
|
|
16
28
|
export * from "./opentargets.js";
|
|
17
|
-
export * from "./
|
|
18
|
-
export * from "./
|
|
29
|
+
export * from "./gene-disease-evidence.js";
|
|
30
|
+
export * from "./drug-gene-interactions.js";
|
|
31
|
+
export * from "./gene-preclinical-profile.js";
|
|
19
32
|
export * from "./search-clinical-trials.js";
|
|
20
33
|
export * from "./search-geo-datasets.js";
|
|
21
|
-
export * from "./search-
|
|
22
|
-
export * from "./
|
|
23
|
-
export * from "./search-disgenet.js";
|
|
24
|
-
export * from "./search-drugbank.js";
|
|
25
|
-
export * from "./search-gwas-catalog.js";
|
|
26
|
-
export * from "./search-bgee-expression.js";
|
|
27
|
-
export * from "./get-impc-ko-profile.js";
|
|
28
|
-
export * from "./check-safety-panel.js";
|
|
34
|
+
export * from "./search-faers.js";
|
|
35
|
+
export * from "./target-safety.js";
|
|
29
36
|
export * from "./comptox.js";
|
package/dist/tools/bio/index.js
CHANGED
|
@@ -1,36 +1,43 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Bio barrel — pure external-API bioinformatics & cheminformatics lookups.
|
|
3
3
|
*
|
|
4
|
-
* Leaf tools with no harness dependencies: each wraps
|
|
5
|
-
* (Ensembl, ChEMBL, PubChem, Open Targets, PubMed, EPA CompTox, …)
|
|
6
|
-
* `defineTool`. Grouped here so the conversation agent imports one
|
|
7
|
-
* rather than
|
|
4
|
+
* Leaf tools with no harness dependencies: each wraps one or more external data
|
|
5
|
+
* sources (Ensembl, ChEMBL, PubChem, Open Targets, PubMed, EPA CompTox, …)
|
|
6
|
+
* behind a `defineTool`. Grouped here so the conversation agent imports one
|
|
7
|
+
* barrel rather than a file per tool.
|
|
8
|
+
*
|
|
9
|
+
* The boundary is the QUESTION, not the database. Where several corpora answer
|
|
10
|
+
* the same question, the corpus is a `sources` parameter and the tool fans out
|
|
11
|
+
* (`gene_disease_evidence`, `drug_gene_interactions`, `target_safety`,
|
|
12
|
+
* `lookup_annotation`, `gene_preclinical_profile`); where one database answers
|
|
13
|
+
* several questions, the endpoint is an `action` parameter (`chembl`,
|
|
14
|
+
* `pubchem`, `pubmed`, `comptox`, `opentargets`, `search_interactions`).
|
|
15
|
+
*
|
|
16
|
+
* Every tool returns a BOUNDED result by default. A default call is sized to be
|
|
17
|
+
* read, not to be complete: limits are small, verbose fields are opt-in, and
|
|
18
|
+
* each result carries the true totals (`totalFound`, `totalInteractions`,
|
|
19
|
+
* `tissueCount`, `outline`, …) so a caller can tell a trimmed answer from a
|
|
20
|
+
* sparse one and widen deliberately.
|
|
8
21
|
*/
|
|
9
|
-
//
|
|
22
|
+
// Identifier resolution
|
|
10
23
|
export * from "./search-gene.js";
|
|
11
|
-
|
|
12
|
-
export * from "./lookup-
|
|
24
|
+
// Functional annotation / networks
|
|
25
|
+
export * from "./lookup-annotation.js";
|
|
13
26
|
export * from "./search-interactions.js";
|
|
14
27
|
// Literature
|
|
15
28
|
export * from "./pubmed.js";
|
|
16
|
-
//
|
|
29
|
+
// Cheminformatics
|
|
17
30
|
export * from "./chembl.js";
|
|
18
|
-
// PubChem
|
|
19
31
|
export * from "./pubchem.js";
|
|
20
|
-
//
|
|
32
|
+
// Target assessment
|
|
21
33
|
export * from "./opentargets.js";
|
|
22
|
-
export * from "./
|
|
23
|
-
export * from "./
|
|
34
|
+
export * from "./gene-disease-evidence.js";
|
|
35
|
+
export * from "./drug-gene-interactions.js";
|
|
36
|
+
export * from "./gene-preclinical-profile.js";
|
|
37
|
+
// Clinical / public-data landscape
|
|
24
38
|
export * from "./search-clinical-trials.js";
|
|
25
39
|
export * from "./search-geo-datasets.js";
|
|
26
|
-
export * from "./search-
|
|
27
|
-
|
|
28
|
-
export * from "./
|
|
29
|
-
export * from "./search-drugbank.js";
|
|
30
|
-
export * from "./search-gwas-catalog.js";
|
|
31
|
-
// Preclinical
|
|
32
|
-
export * from "./search-bgee-expression.js";
|
|
33
|
-
export * from "./get-impc-ko-profile.js";
|
|
34
|
-
// Off-target liability / EPA CompTox
|
|
35
|
-
export * from "./check-safety-panel.js";
|
|
40
|
+
export * from "./search-faers.js";
|
|
41
|
+
// Safety / toxicology
|
|
42
|
+
export * from "./target-safety.js";
|
|
36
43
|
export * from "./comptox.js";
|
package/dist/tools/bio/keys.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Logger } from "../../lib/logger.js";
|
|
1
2
|
import type { Tool } from "../define-tool.js";
|
|
2
3
|
/**
|
|
3
4
|
* API keys for the external bio/chem data sources. Threaded from the
|
|
@@ -14,23 +15,29 @@ export interface BioToolKeys {
|
|
|
14
15
|
readonly github?: string;
|
|
15
16
|
}
|
|
16
17
|
/**
|
|
17
|
-
* The NCBI-backed literature
|
|
18
|
+
* The NCBI-backed literature tool, built from the shared key slice.
|
|
18
19
|
*
|
|
19
20
|
* `pubmed` is the consolidated literature tool (search / details / fulltext
|
|
20
|
-
* behind one `action`)
|
|
21
|
+
* behind one `action`). ClinVar is no longer its own tool — it is one of the
|
|
22
|
+
* corpora behind `gene_disease_evidence`, which also needs the NCBI key.
|
|
21
23
|
*/
|
|
22
24
|
export declare function createNcbiTools(keys: BioToolKeys): {
|
|
23
25
|
pubmed: Tool;
|
|
24
|
-
searchClinvar: Tool;
|
|
25
26
|
};
|
|
26
27
|
/**
|
|
27
|
-
* The
|
|
28
|
+
* The keyed multi-source tools.
|
|
28
29
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
30
|
+
* Each of these fans out over several corpora and needs more than one key, so
|
|
31
|
+
* they are built together from the whole slice rather than per source:
|
|
32
|
+
* `geneDiseaseEvidence` spans the GWAS Catalog (public), DisGeNET (keyed) and
|
|
33
|
+
* ClinVar (NCBI key), and `drugGeneInteractions` spans DGIdb (public), DrugBank
|
|
34
|
+
* (keyed) and PharmGKB (public). A tool whose key is absent degrades that one
|
|
35
|
+
* corpus to `unavailable` in its `perSource` report rather than failing.
|
|
31
36
|
*/
|
|
32
|
-
export declare function createChemDbTools(keys: BioToolKeys
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
export declare function createChemDbTools(keys: BioToolKeys, deps?: {
|
|
38
|
+
readonly logger?: Logger;
|
|
39
|
+
}): {
|
|
40
|
+
geneDiseaseEvidence: Tool;
|
|
41
|
+
drugGeneInteractions: Tool;
|
|
35
42
|
comptox: Tool;
|
|
36
43
|
};
|
package/dist/tools/bio/keys.js
CHANGED
|
@@ -1,30 +1,39 @@
|
|
|
1
|
-
import { createPubMedTool } from "./pubmed.js";
|
|
2
|
-
import { createSearchClinvarTool } from "./search-clinvar.js";
|
|
3
|
-
import { createSearchDrugbankTool } from "./search-drugbank.js";
|
|
4
|
-
import { createSearchDisgenetTool } from "./search-disgenet.js";
|
|
5
1
|
import { createComptoxTool } from "./comptox.js";
|
|
2
|
+
import { createDrugGeneInteractionsTool } from "./drug-gene-interactions.js";
|
|
3
|
+
import { createGeneDiseaseEvidenceTool } from "./gene-disease-evidence.js";
|
|
4
|
+
import { createPubMedTool } from "./pubmed.js";
|
|
6
5
|
/**
|
|
7
|
-
* The NCBI-backed literature
|
|
6
|
+
* The NCBI-backed literature tool, built from the shared key slice.
|
|
8
7
|
*
|
|
9
8
|
* `pubmed` is the consolidated literature tool (search / details / fulltext
|
|
10
|
-
* behind one `action`)
|
|
9
|
+
* behind one `action`). ClinVar is no longer its own tool — it is one of the
|
|
10
|
+
* corpora behind `gene_disease_evidence`, which also needs the NCBI key.
|
|
11
11
|
*/
|
|
12
12
|
export function createNcbiTools(keys) {
|
|
13
13
|
return {
|
|
14
14
|
pubmed: createPubMedTool({ ncbiApiKey: keys.ncbi }),
|
|
15
|
-
searchClinvar: createSearchClinvarTool({ ncbiApiKey: keys.ncbi }),
|
|
16
15
|
};
|
|
17
16
|
}
|
|
18
17
|
/**
|
|
19
|
-
* The
|
|
18
|
+
* The keyed multi-source tools.
|
|
20
19
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* Each of these fans out over several corpora and needs more than one key, so
|
|
21
|
+
* they are built together from the whole slice rather than per source:
|
|
22
|
+
* `geneDiseaseEvidence` spans the GWAS Catalog (public), DisGeNET (keyed) and
|
|
23
|
+
* ClinVar (NCBI key), and `drugGeneInteractions` spans DGIdb (public), DrugBank
|
|
24
|
+
* (keyed) and PharmGKB (public). A tool whose key is absent degrades that one
|
|
25
|
+
* corpus to `unavailable` in its `perSource` report rather than failing.
|
|
23
26
|
*/
|
|
24
|
-
export function createChemDbTools(keys) {
|
|
27
|
+
export function createChemDbTools(keys, deps = {}) {
|
|
25
28
|
return {
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
geneDiseaseEvidence: createGeneDiseaseEvidenceTool({
|
|
30
|
+
disgenetApiKey: keys.disgenet,
|
|
31
|
+
...(keys.ncbi ? { ncbiApiKey: keys.ncbi } : {}),
|
|
32
|
+
}),
|
|
33
|
+
drugGeneInteractions: createDrugGeneInteractionsTool({
|
|
34
|
+
drugbankApiKey: keys.drugbank,
|
|
35
|
+
...(deps.logger ? { logger: deps.logger } : {}),
|
|
36
|
+
}),
|
|
28
37
|
comptox: createComptoxTool({ apiKey: keys.epaCcte }),
|
|
29
38
|
};
|
|
30
39
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lookupAnnotation — one tool over the functional-annotation vocabularies:
|
|
3
|
+
* Gene Ontology (QuickGO) and the pathway databases (KEGG, Reactome).
|
|
4
|
+
*
|
|
5
|
+
* These were two tools asking one question — "what is this gene or term known
|
|
6
|
+
* to do?" — against different vocabularies. `search_pathway` already treated
|
|
7
|
+
* the corpus as a parameter (`source: kegg | reactome | both`) rather than a
|
|
8
|
+
* tool boundary; this extends the same shape to GO.
|
|
9
|
+
*
|
|
10
|
+
* The input is a flat object with a `vocabulary` discriminator — not a
|
|
11
|
+
* `z.discriminatedUnion`, which `defineTool` rejects (model tool calling needs
|
|
12
|
+
* a top-level `"type":"object"`). Per-vocabulary parameters are optional in the
|
|
13
|
+
* schema and made conditionally required by `.refine`, so a malformed call
|
|
14
|
+
* fails at the loop boundary naming the missing field instead of reaching an
|
|
15
|
+
* API request that cannot be built.
|
|
16
|
+
*/
|
|
17
|
+
import { type Pathway } from "../lib/pathway-client.js";
|
|
18
|
+
interface GoTerm {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
definition?: string;
|
|
22
|
+
aspect?: string;
|
|
23
|
+
}
|
|
24
|
+
interface GoAnnotation {
|
|
25
|
+
geneProductId: string;
|
|
26
|
+
goId: string;
|
|
27
|
+
goName: string;
|
|
28
|
+
aspect?: string;
|
|
29
|
+
evidenceCode?: string;
|
|
30
|
+
qualifier?: string;
|
|
31
|
+
}
|
|
32
|
+
type AnnotationOutput = {
|
|
33
|
+
terms?: GoTerm[];
|
|
34
|
+
annotations?: GoAnnotation[];
|
|
35
|
+
} | {
|
|
36
|
+
pathways: Pathway[];
|
|
37
|
+
};
|
|
38
|
+
export declare const lookupAnnotationTool: import("../define-tool.js").Tool<{
|
|
39
|
+
vocabulary: "kegg" | "reactome" | "go" | "pathways";
|
|
40
|
+
query?: string | undefined;
|
|
41
|
+
goId?: string | undefined;
|
|
42
|
+
geneProductId?: string | undefined;
|
|
43
|
+
taxonId?: number | undefined;
|
|
44
|
+
organism?: string | undefined;
|
|
45
|
+
includeGenes?: boolean | undefined;
|
|
46
|
+
limit?: number | undefined;
|
|
47
|
+
}, AnnotationOutput>;
|
|
48
|
+
export {};
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lookupAnnotation — one tool over the functional-annotation vocabularies:
|
|
3
|
+
* Gene Ontology (QuickGO) and the pathway databases (KEGG, Reactome).
|
|
4
|
+
*
|
|
5
|
+
* These were two tools asking one question — "what is this gene or term known
|
|
6
|
+
* to do?" — against different vocabularies. `search_pathway` already treated
|
|
7
|
+
* the corpus as a parameter (`source: kegg | reactome | both`) rather than a
|
|
8
|
+
* tool boundary; this extends the same shape to GO.
|
|
9
|
+
*
|
|
10
|
+
* The input is a flat object with a `vocabulary` discriminator — not a
|
|
11
|
+
* `z.discriminatedUnion`, which `defineTool` rejects (model tool calling needs
|
|
12
|
+
* a top-level `"type":"object"`). Per-vocabulary parameters are optional in the
|
|
13
|
+
* schema and made conditionally required by `.refine`, so a malformed call
|
|
14
|
+
* fails at the loop boundary naming the missing field instead of reaching an
|
|
15
|
+
* API request that cannot be built.
|
|
16
|
+
*/
|
|
17
|
+
import { ok } from "neverthrow";
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
import { defineTool } from "../define-tool.js";
|
|
20
|
+
import { apiFetchValidated, describeApiError } from "../lib/api-utils.js";
|
|
21
|
+
import { searchPathways } from "../lib/pathway-client.js";
|
|
22
|
+
const QUICKGO_BASE = "https://www.ebi.ac.uk/QuickGO/services";
|
|
23
|
+
const HEADERS = { Accept: "application/json" };
|
|
24
|
+
const DEFAULT_LIMIT = 10;
|
|
25
|
+
// QuickGO term response, validated at the fetch boundary. The `.transform`
|
|
26
|
+
// carries the (context-free) rename from the raw wire fields to the `GoTerm[]`
|
|
27
|
+
// we return, so parsing IS both the validation and the normalization — there
|
|
28
|
+
// is no separate raw interface or mapper to keep in sync.
|
|
29
|
+
const GoTermResponseSchema = z
|
|
30
|
+
.object({
|
|
31
|
+
results: z
|
|
32
|
+
.array(z.object({
|
|
33
|
+
id: z.string().optional(),
|
|
34
|
+
name: z.string().optional(),
|
|
35
|
+
definition: z.object({ text: z.string().optional() }).nullable().optional(),
|
|
36
|
+
aspect: z.string().optional(),
|
|
37
|
+
}))
|
|
38
|
+
.optional(),
|
|
39
|
+
})
|
|
40
|
+
.transform((res) => (res.results ?? []).map((t) => ({
|
|
41
|
+
id: t.id ?? "",
|
|
42
|
+
name: t.name ?? "",
|
|
43
|
+
definition: t.definition?.text ?? undefined,
|
|
44
|
+
aspect: t.aspect ?? undefined,
|
|
45
|
+
})));
|
|
46
|
+
const GoAnnotationResponseSchema = z
|
|
47
|
+
.object({
|
|
48
|
+
results: z
|
|
49
|
+
.array(z.object({
|
|
50
|
+
geneProductId: z.string().optional(),
|
|
51
|
+
goId: z.string().optional(),
|
|
52
|
+
goName: z.string().optional(),
|
|
53
|
+
goAspect: z.string().optional(),
|
|
54
|
+
goEvidence: z.string().optional(),
|
|
55
|
+
qualifier: z.string().optional(),
|
|
56
|
+
}))
|
|
57
|
+
.optional(),
|
|
58
|
+
})
|
|
59
|
+
.transform((res) => (res.results ?? []).map((a) => ({
|
|
60
|
+
geneProductId: a.geneProductId ?? "",
|
|
61
|
+
goId: a.goId ?? "",
|
|
62
|
+
goName: a.goName ?? "",
|
|
63
|
+
aspect: a.goAspect ?? undefined,
|
|
64
|
+
evidenceCode: a.goEvidence ?? undefined,
|
|
65
|
+
qualifier: a.qualifier ?? undefined,
|
|
66
|
+
})));
|
|
67
|
+
const inputSchema = z
|
|
68
|
+
.object({
|
|
69
|
+
vocabulary: z
|
|
70
|
+
.enum(["go", "kegg", "reactome", "pathways"])
|
|
71
|
+
.describe("'go' — Gene Ontology via QuickGO (EBI); needs ONE of goId, query, or geneProductId. Returns terms[] { id, name, definition, aspect } " +
|
|
72
|
+
"and/or annotations[] { geneProductId, goId, goName, aspect, evidenceCode, qualifier }. " +
|
|
73
|
+
"'kegg' / 'reactome' — one pathway database; needs query. 'pathways' — both, in parallel; needs query. " +
|
|
74
|
+
"Returns pathways[] { id, name, source, url, genes? }."),
|
|
75
|
+
query: z
|
|
76
|
+
.string()
|
|
77
|
+
.min(1)
|
|
78
|
+
.optional()
|
|
79
|
+
.describe("Free-text term. Required for the pathway vocabularies ('apoptosis', 'MAPK signaling'); for 'go' it is one of the three accepted inputs."),
|
|
80
|
+
goId: z
|
|
81
|
+
.string()
|
|
82
|
+
.regex(/^GO:\d{7}$/)
|
|
83
|
+
.optional()
|
|
84
|
+
.describe("'go' only. A specific GO term ID, e.g. 'GO:0008150'."),
|
|
85
|
+
geneProductId: z.string().min(1).optional().describe("'go' only. A UniProt accession, e.g. 'P04637', for that protein's GO annotations."),
|
|
86
|
+
taxonId: z.number().int().optional().describe("'go' only, with geneProductId. NCBI Taxon ID filter (9606 = human, 10090 = mouse)."),
|
|
87
|
+
organism: z.string().optional().describe("Pathway vocabularies only. KEGG organism code — 'hsa' (human, default), 'mmu' (mouse); mapped for Reactome."),
|
|
88
|
+
includeGenes: z
|
|
89
|
+
.boolean()
|
|
90
|
+
.optional()
|
|
91
|
+
.describe("Pathway vocabularies only. Default FALSE — member-gene lists cost one request per pathway and run to hundreds of symbols. True only when " +
|
|
92
|
+
"you need the members, not the pathway identities."),
|
|
93
|
+
limit: z
|
|
94
|
+
.number()
|
|
95
|
+
.int()
|
|
96
|
+
.min(1)
|
|
97
|
+
.max(100)
|
|
98
|
+
.optional()
|
|
99
|
+
.describe(`Max records per vocabulary queried (default ${DEFAULT_LIMIT}, max 100); with 'pathways' it applies to KEGG and Reactome separately.`),
|
|
100
|
+
})
|
|
101
|
+
.refine((d) => d.vocabulary === "go" || (d.query !== undefined && d.query.trim().length > 0), {
|
|
102
|
+
message: "query is required for vocabulary 'kegg', 'reactome' and 'pathways' — the pathway term to search for",
|
|
103
|
+
path: ["query"],
|
|
104
|
+
})
|
|
105
|
+
.refine((d) => d.vocabulary !== "go" || d.goId !== undefined || d.query !== undefined || d.geneProductId !== undefined, {
|
|
106
|
+
message: "vocabulary 'go' needs at least one of goId (term by ID), query (free-text term search), or geneProductId (a protein's annotations)",
|
|
107
|
+
path: ["goId"],
|
|
108
|
+
})
|
|
109
|
+
.refine((d) => d.vocabulary === "go" || (d.goId === undefined && d.geneProductId === undefined && d.taxonId === undefined), {
|
|
110
|
+
message: "goId, geneProductId and taxonId belong to vocabulary 'go' — the pathway vocabularies take query + organism",
|
|
111
|
+
path: ["vocabulary"],
|
|
112
|
+
})
|
|
113
|
+
.refine((d) => d.vocabulary !== "go" || (d.organism === undefined && d.includeGenes === undefined), {
|
|
114
|
+
message: "organism and includeGenes belong to the pathway vocabularies — vocabulary 'go' filters annotations with taxonId instead",
|
|
115
|
+
path: ["vocabulary"],
|
|
116
|
+
});
|
|
117
|
+
export const lookupAnnotationTool = defineTool({
|
|
118
|
+
id: "lookup_annotation",
|
|
119
|
+
description: "Functional annotation for a gene, protein or term — Gene Ontology (QuickGO) and the pathway databases (KEGG, Reactome) behind one `vocabulary`. " +
|
|
120
|
+
"Names what a gene is known to do, resolves a GO or pathway identifier, or finds the pathways a term belongs to.\n" +
|
|
121
|
+
"This is annotation LOOKUP, not enrichment: to test whether a gene SET is over-represented in GO/KEGG/Reactome terms use " +
|
|
122
|
+
"search_interactions({action:'enrichment'}), which does the statistics.\n" +
|
|
123
|
+
"An empty terms / annotations / pathways array is valid no-data — do not retry the same input.",
|
|
124
|
+
inputSchema,
|
|
125
|
+
execute: async ({ vocabulary, query, goId, geneProductId, taxonId, organism, includeGenes, limit }) => {
|
|
126
|
+
const cap = limit ?? DEFAULT_LIMIT;
|
|
127
|
+
if (vocabulary !== "go") {
|
|
128
|
+
const pathways = await searchPathways(query, {
|
|
129
|
+
source: vocabulary === "pathways" ? "both" : vocabulary,
|
|
130
|
+
organism: organism ?? "hsa",
|
|
131
|
+
includeGenes: includeGenes ?? false,
|
|
132
|
+
maxResults: cap,
|
|
133
|
+
});
|
|
134
|
+
return ok({ pathways });
|
|
135
|
+
}
|
|
136
|
+
const tasks = [];
|
|
137
|
+
let terms;
|
|
138
|
+
let annotations;
|
|
139
|
+
if (goId) {
|
|
140
|
+
tasks.push((async () => {
|
|
141
|
+
const res = await apiFetchValidated(`${QUICKGO_BASE}/ontology/go/terms/${encodeURIComponent(goId)}`, GoTermResponseSchema, {
|
|
142
|
+
headers: HEADERS,
|
|
143
|
+
});
|
|
144
|
+
if (res.isErr())
|
|
145
|
+
throw new Error(`GO lookup: ${describeApiError(res.error)}`);
|
|
146
|
+
terms = res.value;
|
|
147
|
+
})());
|
|
148
|
+
}
|
|
149
|
+
if (query) {
|
|
150
|
+
tasks.push((async () => {
|
|
151
|
+
const params = new URLSearchParams({ query, limit: String(cap) });
|
|
152
|
+
const res = await apiFetchValidated(`${QUICKGO_BASE}/ontology/go/search?${params}`, GoTermResponseSchema, { headers: HEADERS });
|
|
153
|
+
if (res.isErr())
|
|
154
|
+
throw new Error(`GO search: ${describeApiError(res.error)}`);
|
|
155
|
+
const searchTerms = res.value;
|
|
156
|
+
terms = terms ? [...terms, ...searchTerms] : searchTerms;
|
|
157
|
+
})());
|
|
158
|
+
}
|
|
159
|
+
if (geneProductId) {
|
|
160
|
+
tasks.push((async () => {
|
|
161
|
+
const params = new URLSearchParams({ geneProductId, limit: String(cap) });
|
|
162
|
+
if (taxonId)
|
|
163
|
+
params.set("taxonId", String(taxonId));
|
|
164
|
+
const res = await apiFetchValidated(`${QUICKGO_BASE}/annotation/search?${params}`, GoAnnotationResponseSchema, { headers: HEADERS });
|
|
165
|
+
if (res.isErr())
|
|
166
|
+
throw new Error(`Annotations: ${describeApiError(res.error)}`);
|
|
167
|
+
annotations = res.value;
|
|
168
|
+
})());
|
|
169
|
+
}
|
|
170
|
+
await Promise.all(tasks);
|
|
171
|
+
return ok({ terms, annotations });
|
|
172
|
+
},
|
|
173
|
+
});
|