@inflexa-ai/harness 0.16.0 → 0.18.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 +3 -0
- package/dist/agents/conversation-agent.js +5 -2
- package/dist/agents/report-builder.d.ts +2 -0
- package/dist/agents/report-builder.js +2 -0
- package/dist/agents/sandbox/shared.d.ts +3 -0
- package/dist/agents/sandbox/shared.js +5 -0
- package/dist/agents/sandbox/types.d.ts +1 -1
- package/dist/app/chat-turn.d.ts +2 -0
- package/dist/app/chat-turn.js +11 -2
- package/dist/app/spawn-report-session.d.ts +85 -0
- package/dist/app/spawn-report-session.js +83 -0
- package/dist/app/synthesize-run.d.ts +3 -0
- package/dist/app/synthesize-run.js +2 -1
- package/dist/citations/__fixtures__/resolver.d.ts +3 -0
- package/dist/citations/__fixtures__/resolver.js +9 -0
- package/dist/citations/aggregate.d.ts +3 -0
- package/dist/citations/aggregate.js +44 -0
- package/dist/citations/clients/arxiv.d.ts +6 -0
- package/dist/citations/clients/arxiv.js +34 -0
- package/dist/citations/clients/common.d.ts +11 -0
- package/dist/citations/clients/common.js +43 -0
- package/dist/citations/clients/crossref.d.ts +8 -0
- package/dist/citations/clients/crossref.js +98 -0
- package/dist/citations/clients/doi-registry.d.ts +3 -0
- package/dist/citations/clients/doi-registry.js +95 -0
- package/dist/citations/clients/pubmed.d.ts +6 -0
- package/dist/citations/clients/pubmed.js +83 -0
- package/dist/citations/clients/semantic-scholar.d.ts +6 -0
- package/dist/citations/clients/semantic-scholar.js +60 -0
- package/dist/citations/compare.d.ts +9 -0
- package/dist/citations/compare.js +72 -0
- package/dist/citations/match.d.ts +11 -0
- package/dist/citations/match.js +173 -0
- package/dist/citations/normalize.d.ts +31 -0
- package/dist/citations/normalize.js +187 -0
- package/dist/citations/plan.d.ts +2 -0
- package/dist/citations/plan.js +59 -0
- package/dist/citations/resolve.d.ts +28 -0
- package/dist/citations/resolve.js +238 -0
- package/dist/citations/types.d.ts +583 -0
- package/dist/citations/types.js +129 -0
- package/dist/contracts/chat-events.d.ts +9 -0
- package/dist/contracts/index.d.ts +6 -2
- package/dist/contracts/index.js +3 -1
- package/dist/contracts/organ-system.d.ts +49 -0
- package/dist/contracts/organ-system.js +67 -0
- package/dist/contracts/report-blocks.d.ts +564 -0
- package/dist/contracts/report-blocks.js +117 -0
- package/dist/contracts/report-reference.d.ts +459 -0
- package/dist/contracts/report-reference.js +256 -0
- package/dist/contracts/schemas/chat-events.d.ts +2 -0
- package/dist/contracts/schemas/chat-events.js +2 -0
- package/dist/contracts/schemas/chat-parts.d.ts +8 -8
- package/dist/contracts/severity.d.ts +15 -0
- package/dist/contracts/severity.js +10 -0
- package/dist/contracts/target-dossier.d.ts +8673 -12970
- package/dist/contracts/target-dossier.js +534 -591
- package/dist/data/safety-panel-schema.d.ts +18 -5
- package/dist/data/safety-panel-schema.js +4 -4
- package/dist/execution/artifact-metadata.js +1 -0
- package/dist/execution/report-runner.js +1 -3
- package/dist/execution/run-synthesis.d.ts +3 -0
- package/dist/execution/run-synthesis.js +3 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.js +22 -0
- package/dist/lib/async-utils.d.ts +16 -4
- package/dist/lib/async-utils.js +20 -15
- package/dist/lib/cache.d.ts +8 -0
- package/dist/lib/cache.js +35 -0
- package/dist/lib/host-concurrency.js +1 -0
- package/dist/lib/rate-limit.d.ts +35 -0
- package/dist/lib/rate-limit.js +121 -0
- package/dist/literature/sources/arxiv.d.ts +22 -0
- package/dist/literature/sources/arxiv.js +103 -0
- package/dist/literature/sources/http.d.ts +28 -0
- package/dist/literature/sources/http.js +97 -0
- package/dist/literature/sources/pubmed.d.ts +68 -0
- package/dist/literature/sources/pubmed.js +266 -0
- package/dist/literature/sources/semantic-scholar.d.ts +24 -0
- package/dist/literature/sources/semantic-scholar.js +71 -0
- package/dist/loop/__fixtures__/scripted-provider.js +23 -2
- package/dist/loop/run-agent.js +58 -10
- package/dist/loop/tool-detail.d.ts +40 -0
- package/dist/loop/tool-detail.js +35 -3
- package/dist/loop/types.d.ts +13 -0
- package/dist/memory/thread-history.d.ts +10 -0
- package/dist/memory/thread-history.js +12 -1
- package/dist/memory/thread-store.d.ts +142 -25
- package/dist/memory/thread-store.js +228 -40
- package/dist/prompts/literature-reviewer.d.ts +1 -1
- package/dist/prompts/literature-reviewer.js +2 -0
- package/dist/prompts/target-assessment/briefs/executive-recommendation.d.ts +1 -1
- package/dist/prompts/target-assessment/briefs/executive-recommendation.js +5 -5
- package/dist/prompts/target-assessment/briefs/liability-bullets.d.ts +1 -1
- package/dist/prompts/target-assessment/briefs/liability-bullets.js +1 -1
- package/dist/prompts/target-assessment/briefs/target-organ-liabilities.d.ts +1 -1
- package/dist/prompts/target-assessment/briefs/target-organ-liabilities.js +11 -3
- package/dist/prompts/target-assessment/investigation/adversarial-critique.d.ts +1 -0
- package/dist/prompts/target-assessment/investigation/adversarial-critique.js +67 -0
- package/dist/prompts/target-assessment/investigation/claim-reverification.d.ts +1 -0
- package/dist/prompts/target-assessment/investigation/claim-reverification.js +49 -0
- package/dist/prompts/target-assessment/investigation/index.d.ts +3 -0
- package/dist/prompts/target-assessment/investigation/index.js +3 -0
- package/dist/prompts/target-assessment/investigation/mechanism-proposal.d.ts +1 -0
- package/dist/prompts/target-assessment/investigation/mechanism-proposal.js +49 -0
- package/dist/prompts/target-assessment/tox-voice/index.d.ts +1 -1
- package/dist/prompts/target-assessment/tox-voice/vocabulary.d.ts +8 -5
- package/dist/prompts/target-assessment/tox-voice/vocabulary.js +9 -20
- package/dist/providers/__fixtures__/session.d.ts +5 -4
- package/dist/providers/__fixtures__/session.js +4 -4
- package/dist/report-model/block-walk.d.ts +56 -0
- package/dist/report-model/block-walk.js +89 -0
- package/dist/report-model/draft-finish.d.ts +60 -0
- package/dist/report-model/draft-finish.js +56 -0
- package/dist/report-model/draft-operations.d.ts +125 -0
- package/dist/report-model/draft-operations.js +341 -0
- package/dist/report-model/draft-read.d.ts +59 -0
- package/dist/report-model/draft-read.js +109 -0
- package/dist/report-model/draft.d.ts +51 -0
- package/dist/report-model/draft.js +32 -0
- package/dist/report-model/fixture-resolver.d.ts +8 -0
- package/dist/report-model/fixture-resolver.js +252 -0
- package/dist/report-model/mint-snapshot.d.ts +30 -0
- package/dist/report-model/mint-snapshot.js +40 -0
- package/dist/report-model/reference-resolver.d.ts +113 -0
- package/dist/report-model/reference-resolver.js +60 -0
- package/dist/report-model/structural-validation.d.ts +22 -0
- package/dist/report-model/structural-validation.js +68 -0
- package/dist/report-model/validate.d.ts +40 -0
- package/dist/report-model/validate.js +80 -0
- package/dist/report-render/chart.d.ts +33 -0
- package/dist/report-render/chart.js +538 -0
- package/dist/report-render/page.d.ts +127 -0
- package/dist/report-render/page.js +307 -0
- package/dist/report-render/references.d.ts +32 -0
- package/dist/report-render/references.js +44 -0
- package/dist/report-render/render.d.ts +25 -0
- package/dist/report-render/render.js +125 -0
- package/dist/report-render/script-json.d.ts +13 -0
- package/dist/report-render/script-json.js +15 -0
- package/dist/report-render/types.d.ts +39 -0
- package/dist/report-render/types.js +10 -0
- package/dist/report-render/views/chart-view.d.ts +11 -0
- package/dist/report-render/views/chart-view.js +23 -0
- package/dist/report-render/views/page-view.d.ts +20 -0
- package/dist/report-render/views/page-view.js +49 -0
- package/dist/report-render/views/prose.d.ts +28 -0
- package/dist/report-render/views/prose.js +75 -0
- package/dist/report-render/views/references-view.d.ts +20 -0
- package/dist/report-render/views/references-view.js +64 -0
- package/dist/report-render/views/values.d.ts +42 -0
- package/dist/report-render/views/values.js +43 -0
- package/dist/runtime/assemble.d.ts +50 -4
- package/dist/runtime/assemble.js +47 -3
- package/dist/runtime/boot.d.ts +3 -3
- package/dist/runtime/boot.js +22 -5
- package/dist/runtime/dbos.d.ts +4 -4
- package/dist/state/artifacts.d.ts +16 -0
- package/dist/state/artifacts.js +18 -0
- package/dist/state/init.js +77 -1
- package/dist/state/purge-analysis.js +1 -0
- package/dist/state/report-versions.d.ts +132 -0
- package/dist/state/report-versions.js +194 -0
- package/dist/state/schema.d.ts +2 -2
- package/dist/tasks/data-profile.js +1 -0
- package/dist/tools/bio/chembl.js +1 -0
- package/dist/tools/bio/comptox.js +1 -0
- package/dist/tools/bio/drug-gene-interactions.js +1 -0
- package/dist/tools/bio/gene-disease-evidence.js +1 -0
- package/dist/tools/bio/gene-preclinical-profile.d.ts +1 -1
- package/dist/tools/bio/gene-preclinical-profile.js +1 -0
- package/dist/tools/bio/keys.d.ts +4 -2
- package/dist/tools/bio/keys.js +3 -2
- package/dist/tools/bio/lookup-annotation.js +1 -0
- package/dist/tools/bio/opentargets.d.ts +3 -3
- package/dist/tools/bio/opentargets.js +4 -3
- package/dist/tools/bio/pubchem.js +1 -0
- package/dist/tools/bio/pubmed.js +4 -4
- package/dist/tools/bio/search-clinical-trials.js +1 -0
- package/dist/tools/bio/search-faers.js +1 -0
- package/dist/tools/bio/search-geo-datasets.js +1 -0
- package/dist/tools/bio/search-interactions.js +1 -0
- package/dist/tools/bio/target-safety.d.ts +4 -6
- package/dist/tools/bio/target-safety.js +7 -6
- package/dist/tools/define-tool.d.ts +19 -12
- package/dist/tools/define-tool.js +4 -2
- package/dist/tools/display/show-user.js +1 -0
- package/dist/tools/iterate-report.js +2 -0
- package/dist/tools/lib/chembl-client.d.ts +2 -0
- package/dist/tools/lib/chembl-client.js +1 -0
- package/dist/tools/lib/monarch-client.d.ts +42 -0
- package/dist/tools/lib/monarch-client.js +100 -0
- package/dist/tools/lib/ncbi-utils.d.ts +3 -58
- package/dist/tools/lib/ncbi-utils.js +2 -286
- package/dist/tools/lib/pubmed-client.d.ts +5 -12
- package/dist/tools/lib/pubmed-client.js +20 -79
- package/dist/tools/report/build-report.js +1 -0
- package/dist/tools/report/mint-preview-url.js +1 -0
- package/dist/tools/report/preview-snapshot.js +1 -0
- package/dist/tools/report/submit-report.js +1 -0
- package/dist/tools/report/version-fs.js +4 -0
- package/dist/tools/report-authoring/authoring-tools.d.ts +134 -0
- package/dist/tools/report-authoring/authoring-tools.js +352 -0
- package/dist/tools/research/context7-docs.js +2 -0
- package/dist/tools/research/generate-analogy-report.js +3 -2
- package/dist/tools/research/generate-plan.js +3 -0
- package/dist/tools/research/index.d.ts +1 -0
- package/dist/tools/research/index.js +1 -0
- package/dist/tools/research/inspect-data-profile.js +4 -0
- package/dist/tools/research/inspect-run.d.ts +1 -1
- package/dist/tools/research/literature-reviewer.d.ts +3 -0
- package/dist/tools/research/literature-reviewer.js +3 -0
- package/dist/tools/research/resolve-citation.d.ts +3 -0
- package/dist/tools/research/resolve-citation.js +15 -0
- package/dist/tools/research/search-arxiv.d.ts +3 -19
- package/dist/tools/research/search-arxiv.js +10 -105
- package/dist/tools/research/search-github-repos.js +1 -0
- package/dist/tools/research/search-semantic-scholar.d.ts +8 -33
- package/dist/tools/research/search-semantic-scholar.js +30 -88
- package/dist/tools/sandbox/list-available-packages.js +23 -0
- package/dist/tools/sandbox/list-available-refs.js +37 -2
- package/dist/tools/sandbox/report-blocker.js +1 -0
- package/dist/tools/sandbox/skills.js +2 -0
- package/dist/tools/workspace/file-stat.js +1 -0
- package/dist/tools/workspace/show-file.js +2 -0
- package/dist/tools/workspace/show-plan.js +1 -0
- package/dist/workflows/execute-analysis.d.ts +8 -1
- package/dist/workflows/execute-analysis.js +6 -5
- package/dist/workflows/execute-target-assessment.d.ts +13 -2
- package/dist/workflows/execute-target-assessment.js +78 -16
- package/dist/workflows/sandbox-step.d.ts +5 -0
- package/dist/workflows/sandbox-step.js +1 -0
- package/dist/workflows/target-assessment/assemblers/index.d.ts +1 -1
- package/dist/workflows/target-assessment/assemblers/index.js +1 -1
- package/dist/workflows/target-assessment/assemblers/literature.d.ts +9 -27
- package/dist/workflows/target-assessment/assemblers/literature.js +32 -48
- package/dist/workflows/target-assessment/assemblers/orchestrator.d.ts +17 -9
- package/dist/workflows/target-assessment/assemblers/orchestrator.js +195 -85
- package/dist/workflows/target-assessment/assemblers/safety.d.ts +70 -48
- package/dist/workflows/target-assessment/assemblers/safety.js +125 -42
- package/dist/workflows/target-assessment/collectors/index.d.ts +5 -2
- package/dist/workflows/target-assessment/collectors/index.js +48 -5
- package/dist/workflows/target-assessment/coverage.d.ts +43 -27
- package/dist/workflows/target-assessment/coverage.js +33 -29
- package/dist/workflows/target-assessment/fanout/index.js +1 -0
- package/dist/workflows/target-assessment/investigation/index.d.ts +97 -0
- package/dist/workflows/target-assessment/investigation/index.js +419 -0
- package/dist/workflows/target-assessment/lib/approval-precedents.d.ts +26 -5
- package/dist/workflows/target-assessment/lib/approval-precedents.js +95 -29
- package/dist/workflows/target-assessment/lib/citation-validator.js +1 -1
- package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.d.ts +2 -2
- package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.d.ts +2 -3
- package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.js +0 -4
- package/dist/workflows/target-assessment/lib/compute-derived.d.ts +6 -5
- package/dist/workflows/target-assessment/lib/compute-derived.js +14 -5
- package/dist/workflows/target-assessment/lib/family-complex-supplement.d.ts +5 -6
- package/dist/workflows/target-assessment/lib/family-complex-supplement.js +2 -3
- package/dist/workflows/target-assessment/lib/fda-label-safety.d.ts +72 -0
- package/dist/workflows/target-assessment/lib/fda-label-safety.js +166 -0
- package/dist/workflows/target-assessment/lib/heterodimer-filter.d.ts +0 -9
- package/dist/workflows/target-assessment/lib/heterodimer-filter.js +0 -10
- package/dist/workflows/target-assessment/lib/hpo-organ-map.d.ts +34 -0
- package/dist/workflows/target-assessment/lib/hpo-organ-map.js +63 -0
- package/dist/workflows/target-assessment/lib/impc-organ-map.d.ts +19 -0
- package/dist/workflows/target-assessment/lib/impc-organ-map.js +39 -0
- package/dist/workflows/target-assessment/lib/meddra-organ-map.d.ts +24 -14
- package/dist/workflows/target-assessment/lib/meddra-organ-map.js +59 -47
- package/dist/workflows/target-assessment/lib/recommendation-sanitizer.js +1 -1
- package/dist/workflows/target-assessment/lib/regulatory-actions.d.ts +2 -3
- package/dist/workflows/target-assessment/lib/regulatory-actions.js +33 -4
- package/dist/workflows/target-assessment/lib/safety-corroboration.d.ts +44 -0
- package/dist/workflows/target-assessment/lib/safety-corroboration.js +273 -0
- package/dist/workflows/target-assessment/lib/target-identity-filter.d.ts +5 -7
- package/dist/workflows/target-assessment/lib/target-identity-filter.js +5 -7
- package/dist/workflows/target-assessment/metrics.d.ts +1 -6
- package/dist/workflows/target-assessment/metrics.js +1 -13
- package/dist/workflows/target-assessment/phase4-assemble.d.ts +2149 -809
- package/dist/workflows/target-assessment/phase4-assemble.js +19 -19
- package/dist/workflows/target-assessment/phase5-persist.d.ts +20 -21
- package/dist/workflows/target-assessment/phase5-persist.js +56 -173
- package/dist/workflows/target-assessment/schemas.d.ts +48 -3
- package/dist/workflows/target-assessment/schemas.js +19 -0
- package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.d.ts +1 -0
- package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.js +2 -0
- package/dist/workflows/target-assessment/steps/phase2-aggregate.d.ts +27 -63
- package/dist/workflows/target-assessment/steps/phase2-aggregate.js +0 -4
- package/dist/workflows/target-assessment/steps/phase3-aggregate.d.ts +28 -155
- package/dist/workflows/target-assessment/steps/phase3-aggregate.js +0 -6
- package/dist/workflows/target-assessment/steps/synthesis/diagnostics.d.ts +0 -6
- package/dist/workflows/target-assessment/steps/synthesis/diagnostics.js +0 -18
- package/dist/workflows/target-assessment/sub-workflows/per-modulator-polypharm.d.ts +1 -0
- package/dist/workflows/target-assessment/synthesis/index.d.ts +52 -5
- package/dist/workflows/target-assessment/synthesis/index.js +4 -1
- package/dist/workflows/target-assessment/synthesis-probes.d.ts +3 -3
- package/package.json +4 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure async client functions for the Monarch Initiative association API.
|
|
3
|
+
*
|
|
4
|
+
* Monarch curates human gene→phenotype annotations (HPO, from HPOA/OMIM/
|
|
5
|
+
* Orphanet): *human* loss-of-function evidence, meaning what is observed in
|
|
6
|
+
* people carrying variation in the gene, as opposed to a mouse knockout's
|
|
7
|
+
* phenotype or an aggregate gene-disease association score.
|
|
8
|
+
*
|
|
9
|
+
* Each phenotype association carries its HPO ancestor closure, which is what
|
|
10
|
+
* lets a consumer resolve the phenotype onto an organ system by identifier
|
|
11
|
+
* rather than by matching prose.
|
|
12
|
+
*
|
|
13
|
+
* The API is keyless and public.
|
|
14
|
+
*/
|
|
15
|
+
/** A curated human phenotype attributed to variation in the gene. */
|
|
16
|
+
export interface MonarchPhenotypeAssociation {
|
|
17
|
+
/** HPO term id, e.g. `HP:0001392`. */
|
|
18
|
+
readonly hpoId: string;
|
|
19
|
+
readonly label: string;
|
|
20
|
+
/** The term's HPO ancestor closure, for identifier-based organ resolution. */
|
|
21
|
+
readonly ancestorIds: string[];
|
|
22
|
+
/** PMIDs (as `PMID:…` curies) cited by the annotation. */
|
|
23
|
+
readonly publications: string[];
|
|
24
|
+
/** The disease the phenotype was annotated under, when the annotation names one. */
|
|
25
|
+
readonly diseaseContext: string | null;
|
|
26
|
+
/** Share of affected individuals showing the phenotype, when annotated. */
|
|
27
|
+
readonly frequencyPercent: number | null;
|
|
28
|
+
readonly primaryKnowledgeSource: string | null;
|
|
29
|
+
}
|
|
30
|
+
export interface MonarchGeneProfile {
|
|
31
|
+
/** The curie the gene was queried by, e.g. `HGNC:1097`. */
|
|
32
|
+
readonly geneCurie: string;
|
|
33
|
+
readonly phenotypes: MonarchPhenotypeAssociation[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Curated human phenotype associations for one gene.
|
|
37
|
+
*
|
|
38
|
+
* `geneCurie` is a prefixed identifier Monarch resolves — an HGNC id
|
|
39
|
+
* (`HGNC:1097`) or an NCBI gene id (`NCBIGene:673`). A gene Monarch does not
|
|
40
|
+
* hold returns an empty profile rather than an error.
|
|
41
|
+
*/
|
|
42
|
+
export declare function getGenePhenotypeProfile(geneCurie: string): Promise<MonarchGeneProfile>;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure async client functions for the Monarch Initiative association API.
|
|
3
|
+
*
|
|
4
|
+
* Monarch curates human gene→phenotype annotations (HPO, from HPOA/OMIM/
|
|
5
|
+
* Orphanet): *human* loss-of-function evidence, meaning what is observed in
|
|
6
|
+
* people carrying variation in the gene, as opposed to a mouse knockout's
|
|
7
|
+
* phenotype or an aggregate gene-disease association score.
|
|
8
|
+
*
|
|
9
|
+
* Each phenotype association carries its HPO ancestor closure, which is what
|
|
10
|
+
* lets a consumer resolve the phenotype onto an organ system by identifier
|
|
11
|
+
* rather than by matching prose.
|
|
12
|
+
*
|
|
13
|
+
* The API is keyless and public.
|
|
14
|
+
*/
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
import { apiFetchValidated, describeApiError, isUnexpectedApiError } from "./api-utils.js";
|
|
17
|
+
const MONARCH_BASE = "https://api-v3.monarchinitiative.org/v3/api";
|
|
18
|
+
/** Row cap per association category — a well-annotated gene carries a few hundred. */
|
|
19
|
+
const ASSOCIATION_ROW_CAP = 300;
|
|
20
|
+
/** The closure arrays make these responses large; the default timeout is short for them. */
|
|
21
|
+
const REQUEST_TIMEOUT_MS = 120_000;
|
|
22
|
+
const HUMAN_TAXON = "NCBITaxon:9606";
|
|
23
|
+
/** Phenotype terms live in the HPO namespace; a model-organism term would not. */
|
|
24
|
+
const HPO_NAMESPACE = "HP";
|
|
25
|
+
/**
|
|
26
|
+
* One association row. Every field is optional because Monarch returns the same
|
|
27
|
+
* envelope for every category and populates only what applies; the reader below
|
|
28
|
+
* treats a missing field as absent rather than trusting the wire.
|
|
29
|
+
*/
|
|
30
|
+
const MonarchAssociationSchema = z.object({
|
|
31
|
+
subject_taxon: z.string().nullish(),
|
|
32
|
+
object: z.string().optional(),
|
|
33
|
+
object_label: z.string().nullish(),
|
|
34
|
+
object_closure: z.array(z.string()).nullish(),
|
|
35
|
+
publications: z.array(z.string()).nullish(),
|
|
36
|
+
primary_knowledge_source: z.string().nullish(),
|
|
37
|
+
disease_context_qualifier: z.string().nullish(),
|
|
38
|
+
has_percentage: z.number().nullish(),
|
|
39
|
+
});
|
|
40
|
+
const MonarchAssociationPageSchema = z.object({
|
|
41
|
+
items: z.array(MonarchAssociationSchema).optional(),
|
|
42
|
+
});
|
|
43
|
+
async function fetchAssociations(geneCurie, category) {
|
|
44
|
+
const params = new URLSearchParams({
|
|
45
|
+
subject: geneCurie,
|
|
46
|
+
category,
|
|
47
|
+
limit: String(ASSOCIATION_ROW_CAP),
|
|
48
|
+
});
|
|
49
|
+
const res = await apiFetchValidated(`${MONARCH_BASE}/association?${params.toString()}`, MonarchAssociationPageSchema, {
|
|
50
|
+
timeoutMs: REQUEST_TIMEOUT_MS,
|
|
51
|
+
});
|
|
52
|
+
if (res.isErr()) {
|
|
53
|
+
// A 4xx means the gene curie is not one Monarch holds — an expected
|
|
54
|
+
// "not found", returned as an empty page. Anything else is a real
|
|
55
|
+
// failure and surfaces.
|
|
56
|
+
if (isUnexpectedApiError(res.error))
|
|
57
|
+
throw new Error(describeApiError(res.error));
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
return res.value.items ?? [];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Human phenotype annotations only.
|
|
64
|
+
*
|
|
65
|
+
* Monarch serves a gene's phenotype edges from several annotation sets; the
|
|
66
|
+
* filter keeps the rows whose subject is the human gene and whose object is an
|
|
67
|
+
* HPO term, so model-organism phenotypes stay with the collector that owns them.
|
|
68
|
+
*/
|
|
69
|
+
function toPhenotype(row) {
|
|
70
|
+
if (!row.object || !row.object.startsWith(`${HPO_NAMESPACE}:`))
|
|
71
|
+
return null;
|
|
72
|
+
if (row.subject_taxon != null && row.subject_taxon !== HUMAN_TAXON)
|
|
73
|
+
return null;
|
|
74
|
+
return {
|
|
75
|
+
hpoId: row.object,
|
|
76
|
+
label: row.object_label ?? "",
|
|
77
|
+
ancestorIds: row.object_closure ?? [],
|
|
78
|
+
publications: row.publications ?? [],
|
|
79
|
+
diseaseContext: row.disease_context_qualifier ?? null,
|
|
80
|
+
frequencyPercent: row.has_percentage ?? null,
|
|
81
|
+
primaryKnowledgeSource: row.primary_knowledge_source ?? null,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Curated human phenotype associations for one gene.
|
|
86
|
+
*
|
|
87
|
+
* `geneCurie` is a prefixed identifier Monarch resolves — an HGNC id
|
|
88
|
+
* (`HGNC:1097`) or an NCBI gene id (`NCBIGene:673`). A gene Monarch does not
|
|
89
|
+
* hold returns an empty profile rather than an error.
|
|
90
|
+
*/
|
|
91
|
+
export async function getGenePhenotypeProfile(geneCurie) {
|
|
92
|
+
const rows = await fetchAssociations(geneCurie, "biolink:GeneToPhenotypicFeatureAssociation");
|
|
93
|
+
const phenotypes = [];
|
|
94
|
+
for (const row of rows) {
|
|
95
|
+
const p = toPhenotype(row);
|
|
96
|
+
if (p)
|
|
97
|
+
phenotypes.push(p);
|
|
98
|
+
}
|
|
99
|
+
return { geneCurie, phenotypes };
|
|
100
|
+
}
|
|
@@ -1,58 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* Provides base URL constants, API key injection, and XML-to-article
|
|
5
|
-
* mapping functions for PubMed and PMC DTD structures.
|
|
6
|
-
*/
|
|
7
|
-
export declare const NCBI_BASE = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils";
|
|
8
|
-
export declare const NCBI_IDCONV = "https://www.ncbi.nlm.nih.gov/pmc/utils/idconv/v1.0";
|
|
9
|
-
/**
|
|
10
|
-
* Append `api_key` and optional extra params to a base URL.
|
|
11
|
-
* Omits the key when `apiKey` is undefined.
|
|
12
|
-
*/
|
|
13
|
-
export declare function ncbiUrl(apiKey: string | undefined, base: string, params: Record<string, string | number | undefined>): string;
|
|
14
|
-
export interface PubMedSummary {
|
|
15
|
-
pmid: string;
|
|
16
|
-
title: string;
|
|
17
|
-
journal: string;
|
|
18
|
-
year: string;
|
|
19
|
-
authors: string;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Parse esummary XML into PubMedSummary records.
|
|
23
|
-
*/
|
|
24
|
-
export declare function parseEsummary(xml: string): PubMedSummary[];
|
|
25
|
-
export interface ArticleDetail {
|
|
26
|
-
pmid: string;
|
|
27
|
-
title: string;
|
|
28
|
-
abstract: string;
|
|
29
|
-
authors: string[];
|
|
30
|
-
journal: string;
|
|
31
|
-
year: string;
|
|
32
|
-
doi: string;
|
|
33
|
-
meshTerms: string[];
|
|
34
|
-
pmcId: string | null;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Parse efetch PubMed XML into ArticleDetail records.
|
|
38
|
-
* `pmcId` is left null here — resolved separately via ID Converter.
|
|
39
|
-
*/
|
|
40
|
-
export declare function parseEfetch(xml: string): ArticleDetail[];
|
|
41
|
-
/**
|
|
42
|
-
* Parse ID Converter JSON response into a PMID → PMC ID map.
|
|
43
|
-
* Returns only entries that have a PMC ID.
|
|
44
|
-
*/
|
|
45
|
-
export declare function parseIdConvResponse(data: unknown): Map<string, string>;
|
|
46
|
-
export interface ArticleSection {
|
|
47
|
-
heading: string;
|
|
48
|
-
text: string;
|
|
49
|
-
}
|
|
50
|
-
export interface FullTextResult {
|
|
51
|
-
fullText: string;
|
|
52
|
-
sections: ArticleSection[];
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Parse PMC efetch XML into plain text with section structure.
|
|
56
|
-
* Omits figures, tables, and supplementary material markup.
|
|
57
|
-
*/
|
|
58
|
-
export declare function parsePmcFullText(xml: string): FullTextResult | null;
|
|
1
|
+
/** @deprecated Import NCBI authority contracts from literature/sources/pubmed. */
|
|
2
|
+
export { NCBI_BASE, NCBI_IDCONV, ncbiUrl, parseEfetch, parseEsummary, parseIdConvResponse, parsePmcFullText } from "../../literature/sources/pubmed.js";
|
|
3
|
+
export type { ArticleDetail, ArticleSection, FullTextResult, PubMedSummary } from "../../literature/sources/pubmed.js";
|
|
@@ -1,286 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Provides base URL constants, API key injection, and XML-to-article
|
|
5
|
-
* mapping functions for PubMed and PMC DTD structures.
|
|
6
|
-
*/
|
|
7
|
-
import { XMLParser } from "fast-xml-parser";
|
|
8
|
-
// ── NCBI E-utilities base URLs ──────────────────────────────────────
|
|
9
|
-
export const NCBI_BASE = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils";
|
|
10
|
-
export const NCBI_IDCONV = "https://www.ncbi.nlm.nih.gov/pmc/utils/idconv/v1.0";
|
|
11
|
-
// ── API key injection ───────────────────────────────────────────────
|
|
12
|
-
/**
|
|
13
|
-
* Append `api_key` and optional extra params to a base URL.
|
|
14
|
-
* Omits the key when `apiKey` is undefined.
|
|
15
|
-
*/
|
|
16
|
-
export function ncbiUrl(apiKey, base, params) {
|
|
17
|
-
const url = new URL(base);
|
|
18
|
-
if (apiKey) {
|
|
19
|
-
url.searchParams.set("api_key", apiKey);
|
|
20
|
-
}
|
|
21
|
-
for (const [k, v] of Object.entries(params)) {
|
|
22
|
-
if (v !== undefined) {
|
|
23
|
-
url.searchParams.set(k, String(v));
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return url.toString();
|
|
27
|
-
}
|
|
28
|
-
// ── XML parser (shared, configured once) ────────────────────────────
|
|
29
|
-
const xmlParser = new XMLParser({
|
|
30
|
-
ignoreAttributes: false,
|
|
31
|
-
attributeNamePrefix: "@_",
|
|
32
|
-
isArray: (name) => ["PubmedArticle", "Author", "MeshHeading", "AbstractText", "sec", "p", "record"].includes(name),
|
|
33
|
-
textNodeName: "#text",
|
|
34
|
-
});
|
|
35
|
-
function getItemValue(items, name) {
|
|
36
|
-
if (!items)
|
|
37
|
-
return "";
|
|
38
|
-
const arr = Array.isArray(items) ? items : [items];
|
|
39
|
-
const item = arr.find((i) => i["@_Name"] === name);
|
|
40
|
-
return item?.["#text"] ?? "";
|
|
41
|
-
}
|
|
42
|
-
function getAuthorsFromDocSum(items) {
|
|
43
|
-
if (!items)
|
|
44
|
-
return "";
|
|
45
|
-
const arr = Array.isArray(items) ? items : [items];
|
|
46
|
-
const authorList = arr.find((i) => i["@_Name"] === "AuthorList");
|
|
47
|
-
if (!authorList?.Item)
|
|
48
|
-
return "";
|
|
49
|
-
const authors = Array.isArray(authorList.Item) ? authorList.Item : [authorList.Item];
|
|
50
|
-
const names = authors.map((a) => a["#text"] ?? "").filter(Boolean);
|
|
51
|
-
if (names.length === 0)
|
|
52
|
-
return "";
|
|
53
|
-
if (names.length <= 3)
|
|
54
|
-
return names.join(", ");
|
|
55
|
-
return `${names[0]} et al.`;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Parse esummary XML into PubMedSummary records.
|
|
59
|
-
*/
|
|
60
|
-
export function parseEsummary(xml) {
|
|
61
|
-
const parsed = xmlParser.parse(xml);
|
|
62
|
-
const result = parsed?.eSummaryResult ?? parsed;
|
|
63
|
-
const docSums = result?.DocSum;
|
|
64
|
-
if (!docSums)
|
|
65
|
-
return [];
|
|
66
|
-
const docs = Array.isArray(docSums) ? docSums : [docSums];
|
|
67
|
-
return docs.map((doc) => {
|
|
68
|
-
const items = doc.Item;
|
|
69
|
-
const pubDate = getItemValue(items, "PubDate");
|
|
70
|
-
return {
|
|
71
|
-
pmid: String(doc.Id ?? ""),
|
|
72
|
-
title: getItemValue(items, "Title"),
|
|
73
|
-
journal: getItemValue(items, "FullJournalName") || getItemValue(items, "Source"),
|
|
74
|
-
year: pubDate ? String(pubDate).slice(0, 4) : "",
|
|
75
|
-
authors: getAuthorsFromDocSum(items),
|
|
76
|
-
};
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Parse efetch PubMed XML into ArticleDetail records.
|
|
81
|
-
* `pmcId` is left null here — resolved separately via ID Converter.
|
|
82
|
-
*/
|
|
83
|
-
export function parseEfetch(xml) {
|
|
84
|
-
const parsed = xmlParser.parse(xml);
|
|
85
|
-
const articleSet = parsed?.PubmedArticleSet;
|
|
86
|
-
if (!articleSet)
|
|
87
|
-
return [];
|
|
88
|
-
const articles = Array.isArray(articleSet.PubmedArticle) ? articleSet.PubmedArticle : articleSet.PubmedArticle ? [articleSet.PubmedArticle] : [];
|
|
89
|
-
return articles.map((entry) => {
|
|
90
|
-
const article = entry;
|
|
91
|
-
const medlineCitation = article.MedlineCitation;
|
|
92
|
-
const articleData = medlineCitation?.Article;
|
|
93
|
-
// PMID
|
|
94
|
-
const pmidObj = medlineCitation?.PMID;
|
|
95
|
-
const pmid = typeof pmidObj === "object" && pmidObj !== null ? String(pmidObj["#text"] ?? "") : String(pmidObj ?? "");
|
|
96
|
-
// Title
|
|
97
|
-
const title = String(articleData?.ArticleTitle ?? "");
|
|
98
|
-
// Abstract
|
|
99
|
-
const abstractObj = articleData?.Abstract;
|
|
100
|
-
let abstract = "";
|
|
101
|
-
if (abstractObj?.AbstractText) {
|
|
102
|
-
const texts = Array.isArray(abstractObj.AbstractText) ? abstractObj.AbstractText : [abstractObj.AbstractText];
|
|
103
|
-
abstract = texts
|
|
104
|
-
.map((t) => {
|
|
105
|
-
if (typeof t === "string")
|
|
106
|
-
return t;
|
|
107
|
-
if (typeof t === "object" && t !== null) {
|
|
108
|
-
const label = t["@_Label"];
|
|
109
|
-
const text = t["#text"] ?? "";
|
|
110
|
-
return label ? `${label}: ${text}` : String(text);
|
|
111
|
-
}
|
|
112
|
-
return String(t);
|
|
113
|
-
})
|
|
114
|
-
.join("\n\n");
|
|
115
|
-
}
|
|
116
|
-
// Authors
|
|
117
|
-
const authorListObj = articleData?.AuthorList;
|
|
118
|
-
const rawAuthors = authorListObj?.Author;
|
|
119
|
-
const authorArr = Array.isArray(rawAuthors) ? rawAuthors : rawAuthors ? [rawAuthors] : [];
|
|
120
|
-
const authors = authorArr.map((a) => {
|
|
121
|
-
if (a.CollectiveName)
|
|
122
|
-
return a.CollectiveName;
|
|
123
|
-
return [a.ForeName ?? a.Initials, a.LastName].filter(Boolean).join(" ");
|
|
124
|
-
});
|
|
125
|
-
// Journal + year
|
|
126
|
-
const journalObj = articleData?.Journal;
|
|
127
|
-
const journal = String(journalObj?.Title ?? journalObj?.ISOAbbreviation ?? "");
|
|
128
|
-
const journalIssue = journalObj?.JournalIssue;
|
|
129
|
-
const pubDate = journalIssue?.PubDate;
|
|
130
|
-
const year = String(pubDate?.Year ?? "");
|
|
131
|
-
// DOI
|
|
132
|
-
let doi = "";
|
|
133
|
-
const articleIdList = article.PubmedData?.ArticleIdList;
|
|
134
|
-
if (articleIdList?.ArticleId) {
|
|
135
|
-
const ids = Array.isArray(articleIdList.ArticleId) ? articleIdList.ArticleId : [articleIdList.ArticleId];
|
|
136
|
-
for (const idObj of ids) {
|
|
137
|
-
if (typeof idObj === "object" && idObj !== null) {
|
|
138
|
-
const typed = idObj;
|
|
139
|
-
if (typed["@_IdType"] === "doi") {
|
|
140
|
-
doi = String(typed["#text"] ?? "");
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
// MeSH terms
|
|
146
|
-
const meshList = medlineCitation?.MeshHeadingList;
|
|
147
|
-
const meshHeadings = meshList?.MeshHeading
|
|
148
|
-
? Array.isArray(meshList.MeshHeading)
|
|
149
|
-
? meshList.MeshHeading
|
|
150
|
-
: [meshList.MeshHeading]
|
|
151
|
-
: [];
|
|
152
|
-
const meshTerms = meshHeadings
|
|
153
|
-
.map((h) => {
|
|
154
|
-
const desc = h.DescriptorName;
|
|
155
|
-
if (typeof desc === "string")
|
|
156
|
-
return desc;
|
|
157
|
-
if (typeof desc === "object" && desc !== null)
|
|
158
|
-
return desc["#text"] ?? "";
|
|
159
|
-
return "";
|
|
160
|
-
})
|
|
161
|
-
.filter(Boolean);
|
|
162
|
-
return {
|
|
163
|
-
pmid,
|
|
164
|
-
title,
|
|
165
|
-
abstract,
|
|
166
|
-
authors,
|
|
167
|
-
journal,
|
|
168
|
-
year,
|
|
169
|
-
doi,
|
|
170
|
-
meshTerms,
|
|
171
|
-
pmcId: null, // resolved separately via ID Converter
|
|
172
|
-
};
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
// ── NCBI ID Converter mapping ───────────────────────────────────────
|
|
176
|
-
/**
|
|
177
|
-
* Parse ID Converter JSON response into a PMID → PMC ID map.
|
|
178
|
-
* Returns only entries that have a PMC ID.
|
|
179
|
-
*/
|
|
180
|
-
export function parseIdConvResponse(data) {
|
|
181
|
-
const map = new Map();
|
|
182
|
-
const typed = data;
|
|
183
|
-
if (!typed?.records)
|
|
184
|
-
return map;
|
|
185
|
-
for (const rec of typed.records) {
|
|
186
|
-
if (rec.pmid && rec.pmcid) {
|
|
187
|
-
map.set(rec.pmid, rec.pmcid);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
return map;
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Parse PMC efetch XML into plain text with section structure.
|
|
194
|
-
* Omits figures, tables, and supplementary material markup.
|
|
195
|
-
*/
|
|
196
|
-
export function parsePmcFullText(xml) {
|
|
197
|
-
const parsed = xmlParser.parse(xml);
|
|
198
|
-
const articleSet = parsed?.["pmc-articleset"];
|
|
199
|
-
const article = articleSet?.article;
|
|
200
|
-
if (!article)
|
|
201
|
-
return null;
|
|
202
|
-
const body = article.body;
|
|
203
|
-
if (!body)
|
|
204
|
-
return null;
|
|
205
|
-
const sections = [];
|
|
206
|
-
const rawSecs = body.sec ? (Array.isArray(body.sec) ? body.sec : [body.sec]) : [];
|
|
207
|
-
for (const sec of rawSecs) {
|
|
208
|
-
extractSection(sec, sections);
|
|
209
|
-
}
|
|
210
|
-
// If no sections found, try to extract body text directly
|
|
211
|
-
if (sections.length === 0) {
|
|
212
|
-
const bodyText = extractParagraphs(body);
|
|
213
|
-
if (bodyText) {
|
|
214
|
-
sections.push({ heading: "", text: bodyText });
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
const fullText = sections.map((s) => (s.heading ? `## ${s.heading}\n\n${s.text}` : s.text)).join("\n\n");
|
|
218
|
-
return { fullText, sections };
|
|
219
|
-
}
|
|
220
|
-
function extractSection(sec, out) {
|
|
221
|
-
if (!sec || typeof sec !== "object")
|
|
222
|
-
return;
|
|
223
|
-
const s = sec;
|
|
224
|
-
const heading = typeof s.title === "string" ? s.title : "";
|
|
225
|
-
const text = extractParagraphs(s);
|
|
226
|
-
if (text) {
|
|
227
|
-
out.push({ heading, text });
|
|
228
|
-
}
|
|
229
|
-
// Recurse into nested sections
|
|
230
|
-
if (s.sec) {
|
|
231
|
-
const nested = Array.isArray(s.sec) ? s.sec : [s.sec];
|
|
232
|
-
for (const sub of nested) {
|
|
233
|
-
extractSection(sub, out);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
function extractParagraphs(container) {
|
|
238
|
-
const paragraphs = container.p;
|
|
239
|
-
if (!paragraphs)
|
|
240
|
-
return "";
|
|
241
|
-
const pArr = Array.isArray(paragraphs) ? paragraphs : [paragraphs];
|
|
242
|
-
return pArr
|
|
243
|
-
.map((p) => {
|
|
244
|
-
if (typeof p === "string")
|
|
245
|
-
return p;
|
|
246
|
-
if (typeof p === "object" && p !== null) {
|
|
247
|
-
return flattenTextContent(p);
|
|
248
|
-
}
|
|
249
|
-
return String(p);
|
|
250
|
-
})
|
|
251
|
-
.filter(Boolean)
|
|
252
|
-
.join("\n\n");
|
|
253
|
-
}
|
|
254
|
-
/**
|
|
255
|
-
* Recursively extract text content from a parsed XML node,
|
|
256
|
-
* ignoring markup like <fig>, <table-wrap>, <xref>, etc.
|
|
257
|
-
*/
|
|
258
|
-
function flattenTextContent(node) {
|
|
259
|
-
if (typeof node === "string")
|
|
260
|
-
return node;
|
|
261
|
-
if (typeof node === "number")
|
|
262
|
-
return String(node);
|
|
263
|
-
if (!node || typeof node !== "object")
|
|
264
|
-
return "";
|
|
265
|
-
const obj = node;
|
|
266
|
-
const parts = [];
|
|
267
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
268
|
-
// Skip attributes and figure/table elements
|
|
269
|
-
if (key.startsWith("@_"))
|
|
270
|
-
continue;
|
|
271
|
-
if (key === "fig" || key === "table-wrap" || key === "supplementary-material")
|
|
272
|
-
continue;
|
|
273
|
-
if (key === "#text") {
|
|
274
|
-
parts.push(String(value));
|
|
275
|
-
}
|
|
276
|
-
else if (Array.isArray(value)) {
|
|
277
|
-
for (const item of value) {
|
|
278
|
-
parts.push(flattenTextContent(item));
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
else {
|
|
282
|
-
parts.push(flattenTextContent(value));
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
return parts.filter(Boolean).join("");
|
|
286
|
-
}
|
|
1
|
+
/** @deprecated Import NCBI authority contracts from literature/sources/pubmed. */
|
|
2
|
+
export { NCBI_BASE, NCBI_IDCONV, ncbiUrl, parseEfetch, parseEsummary, parseIdConvResponse, parsePmcFullText } from "../../literature/sources/pubmed.js";
|
|
@@ -4,18 +4,11 @@
|
|
|
4
4
|
* Used by §3.11 (Key Papers), §3.10.4 (Preclinical literature), and the
|
|
5
5
|
* pubmed-index Phase-1 collector.
|
|
6
6
|
*/
|
|
7
|
-
import { type ArticleDetail, type ArticleSection, type
|
|
7
|
+
import { type ArticleDetail, type ArticleSection, type FullTextResult, type PubmedSearchOptions, type PubMedSummary } from "../../literature/sources/pubmed.js";
|
|
8
8
|
export type { PubMedSummary, ArticleDetail, ArticleSection, FullTextResult };
|
|
9
|
-
export
|
|
10
|
-
maxResults?: number;
|
|
11
|
-
sort?: "relevance" | "date";
|
|
12
|
-
dateRange?: {
|
|
13
|
-
from: string;
|
|
14
|
-
to: string;
|
|
15
|
-
};
|
|
16
|
-
}
|
|
9
|
+
export type SearchOptions = PubmedSearchOptions;
|
|
17
10
|
/** PubMed search via esearch + esummary. */
|
|
18
|
-
export declare function searchPubmed(ncbiApiKey: string | undefined, query: string, options?: SearchOptions): Promise<{
|
|
11
|
+
export declare function searchPubmed(ncbiApiKey: string | undefined, query: string, options?: SearchOptions, signal?: AbortSignal): Promise<{
|
|
19
12
|
totalFound: number;
|
|
20
13
|
results: PubMedSummary[];
|
|
21
14
|
}>;
|
|
@@ -36,7 +29,7 @@ export interface BoundedArticleDetail extends ArticleDetail {
|
|
|
36
29
|
meshTermCount: number;
|
|
37
30
|
}
|
|
38
31
|
/** Fetch full PubMed article details (efetch + ID Converter for PMC). */
|
|
39
|
-
export declare function getArticleDetails(ncbiApiKey: string | undefined, pmids: string[]): Promise<{
|
|
32
|
+
export declare function getArticleDetails(ncbiApiKey: string | undefined, pmids: string[], signal?: AbortSignal): Promise<{
|
|
40
33
|
articles: ArticleDetail[];
|
|
41
34
|
notFound: string[];
|
|
42
35
|
}>;
|
|
@@ -76,6 +69,6 @@ export interface BoundedFullText {
|
|
|
76
69
|
* Methods is worse than none) and admitted in document order until the budget
|
|
77
70
|
* would be exceeded.
|
|
78
71
|
*/
|
|
79
|
-
export declare function getArticleFullText(ncbiApiKey: string | undefined, pmcId: string, options?: FullTextOptions): Promise<BoundedFullText | null>;
|
|
72
|
+
export declare function getArticleFullText(ncbiApiKey: string | undefined, pmcId: string, options?: FullTextOptions, signal?: AbortSignal): Promise<BoundedFullText | null>;
|
|
80
73
|
/** Apply the section filter and character budget to a parsed full text. */
|
|
81
74
|
export declare function boundFullText(parsed: FullTextResult, options?: FullTextOptions): BoundedFullText;
|
|
@@ -4,76 +4,27 @@
|
|
|
4
4
|
* Used by §3.11 (Key Papers), §3.10.4 (Preclinical literature), and the
|
|
5
5
|
* pubmed-index Phase-1 collector.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
});
|
|
15
|
-
const IdConvResponseSchema = z.object({
|
|
16
|
-
records: z.array(z.object({ pmid: z.string().optional(), pmcid: z.string().optional() })).optional(),
|
|
17
|
-
});
|
|
18
|
-
/** PubMed search via esearch + esummary. */
|
|
19
|
-
export async function searchPubmed(ncbiApiKey, query, options = {}) {
|
|
20
|
-
const maxResults = options.maxResults ?? 10;
|
|
21
|
-
const sort = options.sort ?? "relevance";
|
|
22
|
-
const esearchParams = {
|
|
23
|
-
db: "pubmed",
|
|
24
|
-
term: query,
|
|
25
|
-
retmax: maxResults,
|
|
26
|
-
retmode: "json",
|
|
27
|
-
sort: sort === "date" ? "pub+date" : "relevance",
|
|
28
|
-
};
|
|
29
|
-
if (options.dateRange) {
|
|
30
|
-
esearchParams.mindate = options.dateRange.from;
|
|
31
|
-
esearchParams.maxdate = options.dateRange.to;
|
|
32
|
-
esearchParams.datetype = "pdat";
|
|
33
|
-
}
|
|
34
|
-
const searchUrl = ncbiUrl(ncbiApiKey, `${NCBI_BASE}/esearch.fcgi`, esearchParams);
|
|
35
|
-
const searchResult = await apiFetchValidated(searchUrl, EsearchResponseSchema);
|
|
36
|
-
if (searchResult.isErr())
|
|
37
|
-
throw new Error(describeApiError(searchResult.error));
|
|
38
|
-
const idList = searchResult.value.esearchresult?.idlist ?? [];
|
|
39
|
-
const totalFound = parseInt(searchResult.value.esearchresult?.count ?? "0", 10);
|
|
40
|
-
if (idList.length === 0)
|
|
41
|
-
return { totalFound, results: [] };
|
|
42
|
-
const summaryUrl = ncbiUrl(ncbiApiKey, `${NCBI_BASE}/esummary.fcgi`, {
|
|
43
|
-
db: "pubmed",
|
|
44
|
-
id: idList.join(","),
|
|
45
|
-
retmode: "xml",
|
|
7
|
+
import { createPubmedSource, } from "../../literature/sources/pubmed.js";
|
|
8
|
+
function pubmedSource(ncbiApiKey) {
|
|
9
|
+
return createPubmedSource({
|
|
10
|
+
...(ncbiApiKey === undefined ? {} : { apiKey: ncbiApiKey }),
|
|
11
|
+
maxRetries: 3,
|
|
12
|
+
retryDelayMs: 1_000,
|
|
13
|
+
timeoutMs: 90_000,
|
|
46
14
|
});
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
15
|
+
}
|
|
16
|
+
function unwrap(result) {
|
|
17
|
+
if (result.status !== "ok")
|
|
18
|
+
throw new Error(result.detail);
|
|
19
|
+
return result.value;
|
|
20
|
+
}
|
|
21
|
+
/** PubMed search via esearch + esummary. */
|
|
22
|
+
export async function searchPubmed(ncbiApiKey, query, options = {}, signal) {
|
|
23
|
+
return unwrap(await pubmedSource(ncbiApiKey).search(query, options, signal));
|
|
51
24
|
}
|
|
52
25
|
/** Fetch full PubMed article details (efetch + ID Converter for PMC). */
|
|
53
|
-
export async function getArticleDetails(ncbiApiKey, pmids) {
|
|
54
|
-
|
|
55
|
-
const efetchAsync = apiFetch(ncbiUrl(ncbiApiKey, `${NCBI_BASE}/efetch.fcgi`, {
|
|
56
|
-
db: "pubmed",
|
|
57
|
-
id: idString,
|
|
58
|
-
rettype: "xml",
|
|
59
|
-
retmode: "xml",
|
|
60
|
-
}), { parseAs: "text" });
|
|
61
|
-
const idConvAsync = apiFetchValidated(ncbiUrl(ncbiApiKey, NCBI_IDCONV, { ids: idString, format: "json" }), IdConvResponseSchema);
|
|
62
|
-
// Both requests run concurrently; each Result is awaited and handled below.
|
|
63
|
-
const efetchResult = await efetchAsync;
|
|
64
|
-
const idConvResult = await idConvAsync;
|
|
65
|
-
if (efetchResult.isErr())
|
|
66
|
-
throw new Error(describeApiError(efetchResult.error));
|
|
67
|
-
const articles = parseEfetch(efetchResult.value);
|
|
68
|
-
if (idConvResult.isOk()) {
|
|
69
|
-
const pmcMap = parseIdConvResponse(idConvResult.value);
|
|
70
|
-
for (const article of articles) {
|
|
71
|
-
article.pmcId = pmcMap.get(article.pmid) ?? null;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
const foundPmids = new Set(articles.map((a) => a.pmid));
|
|
75
|
-
const notFound = pmids.filter((id) => !foundPmids.has(id));
|
|
76
|
-
return { articles, notFound };
|
|
26
|
+
export async function getArticleDetails(ncbiApiKey, pmids, signal) {
|
|
27
|
+
return unwrap(await pubmedSource(ncbiApiKey).articleDetails(pmids, signal));
|
|
77
28
|
}
|
|
78
29
|
/** Trim the two unbounded list fields on each article, recording their true sizes. */
|
|
79
30
|
export function boundArticleDetails(articles, options = {}) {
|
|
@@ -97,18 +48,8 @@ export function boundArticleDetails(articles, options = {}) {
|
|
|
97
48
|
* Methods is worse than none) and admitted in document order until the budget
|
|
98
49
|
* would be exceeded.
|
|
99
50
|
*/
|
|
100
|
-
export async function getArticleFullText(ncbiApiKey, pmcId, options = {}) {
|
|
101
|
-
const
|
|
102
|
-
const url = ncbiUrl(ncbiApiKey, `${NCBI_BASE}/efetch.fcgi`, {
|
|
103
|
-
db: "pmc",
|
|
104
|
-
id: numericId,
|
|
105
|
-
rettype: "xml",
|
|
106
|
-
retmode: "xml",
|
|
107
|
-
});
|
|
108
|
-
const result = await apiFetch(url, { parseAs: "text" });
|
|
109
|
-
if (result.isErr())
|
|
110
|
-
throw new Error(describeApiError(result.error));
|
|
111
|
-
const parsed = parsePmcFullText(result.value);
|
|
51
|
+
export async function getArticleFullText(ncbiApiKey, pmcId, options = {}, signal) {
|
|
52
|
+
const parsed = unwrap(await pubmedSource(ncbiApiKey).fetchFullText(pmcId, signal));
|
|
112
53
|
if (!parsed)
|
|
113
54
|
return null;
|
|
114
55
|
return boundFullText(parsed, options);
|
|
@@ -16,6 +16,7 @@ export function createBuildReportTool(state) {
|
|
|
16
16
|
"Always use this — never invoke a build script directly. Returns " +
|
|
17
17
|
"structured errors with line numbers when the template is broken.",
|
|
18
18
|
inputSchema: z.object({}),
|
|
19
|
+
describeCall: "none",
|
|
19
20
|
execute: async () => {
|
|
20
21
|
return ok(await renderReport({
|
|
21
22
|
versionDir: state.versionDir,
|
|
@@ -24,6 +24,7 @@ export function createMintPreviewUrlTool(state) {
|
|
|
24
24
|
inputSchema: z.object({
|
|
25
25
|
version: z.number().optional().describe("Version to mint for. Defaults to the iteration's current version."),
|
|
26
26
|
}),
|
|
27
|
+
describeCall: "none",
|
|
27
28
|
execute: async (input) => {
|
|
28
29
|
const version = input.version ?? state.currentVersion;
|
|
29
30
|
const result = await state.previews.mintPreviewAccess(state.resourceId, state.previewId);
|
|
@@ -47,6 +47,7 @@ export function createPreviewSnapshotTool(state) {
|
|
|
47
47
|
.optional()
|
|
48
48
|
.describe("Optional extra wait in ms after the theme-ready event (e.g., for chart paint). Capped at 30s."),
|
|
49
49
|
}),
|
|
50
|
+
describeCall: "none",
|
|
50
51
|
execute: async (input) => {
|
|
51
52
|
let url = state.urlCell.url;
|
|
52
53
|
const expiresAt = state.urlCell.expiresAt;
|