aurelian 0.3.2__py3-none-any.whl
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.
- aurelian/__init__.py +9 -0
- aurelian/agents/__init__.py +0 -0
- aurelian/agents/amigo/__init__.py +3 -0
- aurelian/agents/amigo/amigo_agent.py +77 -0
- aurelian/agents/amigo/amigo_config.py +85 -0
- aurelian/agents/amigo/amigo_evals.py +73 -0
- aurelian/agents/amigo/amigo_gradio.py +52 -0
- aurelian/agents/amigo/amigo_mcp.py +152 -0
- aurelian/agents/amigo/amigo_tools.py +152 -0
- aurelian/agents/biblio/__init__.py +42 -0
- aurelian/agents/biblio/biblio_agent.py +94 -0
- aurelian/agents/biblio/biblio_config.py +40 -0
- aurelian/agents/biblio/biblio_gradio.py +67 -0
- aurelian/agents/biblio/biblio_mcp.py +115 -0
- aurelian/agents/biblio/biblio_tools.py +164 -0
- aurelian/agents/biblio_agent.py +46 -0
- aurelian/agents/checklist/__init__.py +44 -0
- aurelian/agents/checklist/checklist_agent.py +85 -0
- aurelian/agents/checklist/checklist_config.py +28 -0
- aurelian/agents/checklist/checklist_gradio.py +70 -0
- aurelian/agents/checklist/checklist_mcp.py +86 -0
- aurelian/agents/checklist/checklist_tools.py +141 -0
- aurelian/agents/checklist/content/checklists.yaml +7 -0
- aurelian/agents/checklist/content/streams.csv +136 -0
- aurelian/agents/checklist_agent.py +40 -0
- aurelian/agents/chemistry/__init__.py +3 -0
- aurelian/agents/chemistry/chemistry_agent.py +46 -0
- aurelian/agents/chemistry/chemistry_config.py +71 -0
- aurelian/agents/chemistry/chemistry_evals.py +79 -0
- aurelian/agents/chemistry/chemistry_gradio.py +50 -0
- aurelian/agents/chemistry/chemistry_mcp.py +120 -0
- aurelian/agents/chemistry/chemistry_tools.py +121 -0
- aurelian/agents/chemistry/image_agent.py +15 -0
- aurelian/agents/d4d/__init__.py +30 -0
- aurelian/agents/d4d/d4d_agent.py +72 -0
- aurelian/agents/d4d/d4d_config.py +46 -0
- aurelian/agents/d4d/d4d_gradio.py +58 -0
- aurelian/agents/d4d/d4d_mcp.py +71 -0
- aurelian/agents/d4d/d4d_tools.py +157 -0
- aurelian/agents/d4d_agent.py +64 -0
- aurelian/agents/diagnosis/__init__.py +33 -0
- aurelian/agents/diagnosis/diagnosis_agent.py +53 -0
- aurelian/agents/diagnosis/diagnosis_config.py +48 -0
- aurelian/agents/diagnosis/diagnosis_evals.py +76 -0
- aurelian/agents/diagnosis/diagnosis_gradio.py +52 -0
- aurelian/agents/diagnosis/diagnosis_mcp.py +141 -0
- aurelian/agents/diagnosis/diagnosis_tools.py +204 -0
- aurelian/agents/diagnosis_agent.py +28 -0
- aurelian/agents/draw/__init__.py +3 -0
- aurelian/agents/draw/draw_agent.py +39 -0
- aurelian/agents/draw/draw_config.py +26 -0
- aurelian/agents/draw/draw_gradio.py +50 -0
- aurelian/agents/draw/draw_mcp.py +94 -0
- aurelian/agents/draw/draw_tools.py +100 -0
- aurelian/agents/draw/judge_agent.py +18 -0
- aurelian/agents/filesystem/__init__.py +0 -0
- aurelian/agents/filesystem/filesystem_config.py +27 -0
- aurelian/agents/filesystem/filesystem_gradio.py +49 -0
- aurelian/agents/filesystem/filesystem_mcp.py +89 -0
- aurelian/agents/filesystem/filesystem_tools.py +95 -0
- aurelian/agents/filesystem/py.typed +0 -0
- aurelian/agents/github/__init__.py +0 -0
- aurelian/agents/github/github_agent.py +83 -0
- aurelian/agents/github/github_cli.py +248 -0
- aurelian/agents/github/github_config.py +22 -0
- aurelian/agents/github/github_gradio.py +152 -0
- aurelian/agents/github/github_mcp.py +252 -0
- aurelian/agents/github/github_tools.py +408 -0
- aurelian/agents/github/github_tools.py.tmp +413 -0
- aurelian/agents/goann/__init__.py +13 -0
- aurelian/agents/goann/documents/Transcription_Factors_Annotation_Guidelines.md +1000 -0
- aurelian/agents/goann/documents/Transcription_Factors_Annotation_Guidelines.pdf +0 -0
- aurelian/agents/goann/documents/Transcription_Factors_Annotation_Guidelines_Paper.md +693 -0
- aurelian/agents/goann/documents/Transcription_Factors_Annotation_Guidelines_Paper.pdf +0 -0
- aurelian/agents/goann/goann_agent.py +90 -0
- aurelian/agents/goann/goann_config.py +90 -0
- aurelian/agents/goann/goann_evals.py +104 -0
- aurelian/agents/goann/goann_gradio.py +62 -0
- aurelian/agents/goann/goann_mcp.py +0 -0
- aurelian/agents/goann/goann_tools.py +65 -0
- aurelian/agents/gocam/__init__.py +43 -0
- aurelian/agents/gocam/documents/DNA-binding transcription factor activity annotation guidelines.docx +0 -0
- aurelian/agents/gocam/documents/DNA-binding transcription factor activity annotation guidelines.pdf +0 -0
- aurelian/agents/gocam/documents/DNA-binding_transcription_factor_activity_annotation_guidelines.md +100 -0
- aurelian/agents/gocam/documents/E3 ubiquitin ligases.docx +0 -0
- aurelian/agents/gocam/documents/E3 ubiquitin ligases.pdf +0 -0
- aurelian/agents/gocam/documents/E3_ubiquitin_ligases.md +134 -0
- aurelian/agents/gocam/documents/GO-CAM annotation guidelines README.docx +0 -0
- aurelian/agents/gocam/documents/GO-CAM annotation guidelines README.pdf +0 -0
- aurelian/agents/gocam/documents/GO-CAM modelling guidelines TO DO.docx +0 -0
- aurelian/agents/gocam/documents/GO-CAM modelling guidelines TO DO.pdf +0 -0
- aurelian/agents/gocam/documents/GO-CAM_annotation_guidelines_README.md +1 -0
- aurelian/agents/gocam/documents/GO-CAM_modelling_guidelines_TO_DO.md +3 -0
- aurelian/agents/gocam/documents/How to annotate complexes in GO-CAM.docx +0 -0
- aurelian/agents/gocam/documents/How to annotate complexes in GO-CAM.pdf +0 -0
- aurelian/agents/gocam/documents/How to annotate molecular adaptors.docx +0 -0
- aurelian/agents/gocam/documents/How to annotate molecular adaptors.pdf +0 -0
- aurelian/agents/gocam/documents/How to annotate sequestering proteins.docx +0 -0
- aurelian/agents/gocam/documents/How to annotate sequestering proteins.pdf +0 -0
- aurelian/agents/gocam/documents/How_to_annotate_complexes_in_GO-CAM.md +29 -0
- aurelian/agents/gocam/documents/How_to_annotate_molecular_adaptors.md +31 -0
- aurelian/agents/gocam/documents/How_to_annotate_sequestering_proteins.md +42 -0
- aurelian/agents/gocam/documents/Molecular adaptor activity.docx +0 -0
- aurelian/agents/gocam/documents/Molecular adaptor activity.pdf +0 -0
- aurelian/agents/gocam/documents/Molecular carrier activity.docx +0 -0
- aurelian/agents/gocam/documents/Molecular carrier activity.pdf +0 -0
- aurelian/agents/gocam/documents/Molecular_adaptor_activity.md +51 -0
- aurelian/agents/gocam/documents/Molecular_carrier_activity.md +41 -0
- aurelian/agents/gocam/documents/Protein sequestering activity.docx +0 -0
- aurelian/agents/gocam/documents/Protein sequestering activity.pdf +0 -0
- aurelian/agents/gocam/documents/Protein_sequestering_activity.md +50 -0
- aurelian/agents/gocam/documents/Signaling receptor activity annotation guidelines.docx +0 -0
- aurelian/agents/gocam/documents/Signaling receptor activity annotation guidelines.pdf +0 -0
- aurelian/agents/gocam/documents/Signaling_receptor_activity_annotation_guidelines.md +187 -0
- aurelian/agents/gocam/documents/Transcription coregulator activity.docx +0 -0
- aurelian/agents/gocam/documents/Transcription coregulator activity.pdf +0 -0
- aurelian/agents/gocam/documents/Transcription_coregulator_activity.md +36 -0
- aurelian/agents/gocam/documents/Transporter activity annotation annotation guidelines.docx +0 -0
- aurelian/agents/gocam/documents/Transporter activity annotation annotation guidelines.pdf +0 -0
- aurelian/agents/gocam/documents/Transporter_activity_annotation_annotation_guidelines.md +43 -0
- Regulatory Processes in GO-CAM.docx +0 -0
- Regulatory Processes in GO-CAM.pdf +0 -0
- aurelian/agents/gocam/documents/WIP_-_Regulation_and_Regulatory_Processes_in_GO-CAM.md +31 -0
- aurelian/agents/gocam/documents/md/DNA-binding_transcription_factor_activity_annotation_guidelines.md +131 -0
- aurelian/agents/gocam/documents/md/E3_ubiquitin_ligases.md +166 -0
- aurelian/agents/gocam/documents/md/GO-CAM_annotation_guidelines_README.md +1 -0
- aurelian/agents/gocam/documents/md/GO-CAM_modelling_guidelines_TO_DO.md +5 -0
- aurelian/agents/gocam/documents/md/How_to_annotate_complexes_in_GO-CAM.md +28 -0
- aurelian/agents/gocam/documents/md/How_to_annotate_molecular_adaptors.md +19 -0
- aurelian/agents/gocam/documents/md/How_to_annotate_sequestering_proteins.md +38 -0
- aurelian/agents/gocam/documents/md/Molecular_adaptor_activity.md +52 -0
- aurelian/agents/gocam/documents/md/Molecular_carrier_activity.md +59 -0
- aurelian/agents/gocam/documents/md/Protein_sequestering_activity.md +52 -0
- aurelian/agents/gocam/documents/md/Signaling_receptor_activity_annotation_guidelines.md +271 -0
- aurelian/agents/gocam/documents/md/Transcription_coregulator_activity.md +54 -0
- aurelian/agents/gocam/documents/md/Transporter_activity_annotation_annotation_guidelines.md +38 -0
- aurelian/agents/gocam/documents/md/WIP_-_Regulation_and_Regulatory_Processes_in_GO-CAM.md +39 -0
- aurelian/agents/gocam/documents/pandoc_md/Signaling_receptor_activity_annotation_guidelines.md +334 -0
- aurelian/agents/gocam/gocam_agent.py +240 -0
- aurelian/agents/gocam/gocam_config.py +85 -0
- aurelian/agents/gocam/gocam_curator_agent.py +46 -0
- aurelian/agents/gocam/gocam_evals.py +67 -0
- aurelian/agents/gocam/gocam_gradio.py +89 -0
- aurelian/agents/gocam/gocam_mcp.py +224 -0
- aurelian/agents/gocam/gocam_tools.py +294 -0
- aurelian/agents/linkml/__init__.py +0 -0
- aurelian/agents/linkml/linkml_agent.py +62 -0
- aurelian/agents/linkml/linkml_config.py +48 -0
- aurelian/agents/linkml/linkml_evals.py +66 -0
- aurelian/agents/linkml/linkml_gradio.py +45 -0
- aurelian/agents/linkml/linkml_mcp.py +186 -0
- aurelian/agents/linkml/linkml_tools.py +102 -0
- aurelian/agents/literature/__init__.py +3 -0
- aurelian/agents/literature/literature_agent.py +55 -0
- aurelian/agents/literature/literature_config.py +35 -0
- aurelian/agents/literature/literature_gradio.py +52 -0
- aurelian/agents/literature/literature_mcp.py +174 -0
- aurelian/agents/literature/literature_tools.py +182 -0
- aurelian/agents/monarch/__init__.py +25 -0
- aurelian/agents/monarch/monarch_agent.py +44 -0
- aurelian/agents/monarch/monarch_config.py +45 -0
- aurelian/agents/monarch/monarch_gradio.py +51 -0
- aurelian/agents/monarch/monarch_mcp.py +65 -0
- aurelian/agents/monarch/monarch_tools.py +113 -0
- aurelian/agents/oak/__init__.py +0 -0
- aurelian/agents/oak/oak_config.py +27 -0
- aurelian/agents/oak/oak_gradio.py +57 -0
- aurelian/agents/ontology_mapper/__init__.py +31 -0
- aurelian/agents/ontology_mapper/ontology_mapper_agent.py +56 -0
- aurelian/agents/ontology_mapper/ontology_mapper_config.py +50 -0
- aurelian/agents/ontology_mapper/ontology_mapper_evals.py +108 -0
- aurelian/agents/ontology_mapper/ontology_mapper_gradio.py +58 -0
- aurelian/agents/ontology_mapper/ontology_mapper_mcp.py +81 -0
- aurelian/agents/ontology_mapper/ontology_mapper_tools.py +147 -0
- aurelian/agents/phenopackets/__init__.py +3 -0
- aurelian/agents/phenopackets/phenopackets_agent.py +58 -0
- aurelian/agents/phenopackets/phenopackets_config.py +72 -0
- aurelian/agents/phenopackets/phenopackets_evals.py +99 -0
- aurelian/agents/phenopackets/phenopackets_gradio.py +55 -0
- aurelian/agents/phenopackets/phenopackets_mcp.py +178 -0
- aurelian/agents/phenopackets/phenopackets_tools.py +127 -0
- aurelian/agents/rag/__init__.py +40 -0
- aurelian/agents/rag/rag_agent.py +83 -0
- aurelian/agents/rag/rag_config.py +80 -0
- aurelian/agents/rag/rag_gradio.py +67 -0
- aurelian/agents/rag/rag_mcp.py +107 -0
- aurelian/agents/rag/rag_tools.py +189 -0
- aurelian/agents/rag_agent.py +54 -0
- aurelian/agents/robot/__init__.py +0 -0
- aurelian/agents/robot/assets/__init__.py +3 -0
- aurelian/agents/robot/assets/template.md +384 -0
- aurelian/agents/robot/robot_config.py +25 -0
- aurelian/agents/robot/robot_gradio.py +46 -0
- aurelian/agents/robot/robot_mcp.py +100 -0
- aurelian/agents/robot/robot_ontology_agent.py +139 -0
- aurelian/agents/robot/robot_tools.py +50 -0
- aurelian/agents/talisman/__init__.py +3 -0
- aurelian/agents/talisman/talisman_agent.py +126 -0
- aurelian/agents/talisman/talisman_config.py +66 -0
- aurelian/agents/talisman/talisman_gradio.py +50 -0
- aurelian/agents/talisman/talisman_mcp.py +168 -0
- aurelian/agents/talisman/talisman_tools.py +720 -0
- aurelian/agents/ubergraph/__init__.py +40 -0
- aurelian/agents/ubergraph/ubergraph_agent.py +71 -0
- aurelian/agents/ubergraph/ubergraph_config.py +79 -0
- aurelian/agents/ubergraph/ubergraph_gradio.py +48 -0
- aurelian/agents/ubergraph/ubergraph_mcp.py +69 -0
- aurelian/agents/ubergraph/ubergraph_tools.py +118 -0
- aurelian/agents/uniprot/__init__.py +37 -0
- aurelian/agents/uniprot/uniprot_agent.py +43 -0
- aurelian/agents/uniprot/uniprot_config.py +43 -0
- aurelian/agents/uniprot/uniprot_evals.py +99 -0
- aurelian/agents/uniprot/uniprot_gradio.py +48 -0
- aurelian/agents/uniprot/uniprot_mcp.py +168 -0
- aurelian/agents/uniprot/uniprot_tools.py +136 -0
- aurelian/agents/web/__init__.py +0 -0
- aurelian/agents/web/web_config.py +27 -0
- aurelian/agents/web/web_gradio.py +48 -0
- aurelian/agents/web/web_mcp.py +50 -0
- aurelian/agents/web/web_tools.py +108 -0
- aurelian/chat.py +23 -0
- aurelian/cli.py +800 -0
- aurelian/dependencies/__init__.py +0 -0
- aurelian/dependencies/workdir.py +78 -0
- aurelian/mcp/__init__.py +0 -0
- aurelian/mcp/amigo_mcp_test.py +86 -0
- aurelian/mcp/config_generator.py +123 -0
- aurelian/mcp/example_config.json +43 -0
- aurelian/mcp/generate_sample_config.py +37 -0
- aurelian/mcp/gocam_mcp_test.py +126 -0
- aurelian/mcp/linkml_mcp_tools.py +190 -0
- aurelian/mcp/mcp_discovery.py +87 -0
- aurelian/mcp/mcp_test.py +31 -0
- aurelian/mcp/phenopackets_mcp_test.py +103 -0
- aurelian/tools/__init__.py +0 -0
- aurelian/tools/web/__init__.py +0 -0
- aurelian/tools/web/url_download.py +51 -0
- aurelian/utils/__init__.py +0 -0
- aurelian/utils/async_utils.py +15 -0
- aurelian/utils/data_utils.py +32 -0
- aurelian/utils/documentation_manager.py +59 -0
- aurelian/utils/doi_fetcher.py +238 -0
- aurelian/utils/ontology_utils.py +68 -0
- aurelian/utils/pdf_fetcher.py +23 -0
- aurelian/utils/process_logs.py +100 -0
- aurelian/utils/pubmed_utils.py +238 -0
- aurelian/utils/pytest_report_to_markdown.py +67 -0
- aurelian/utils/robot_ontology_utils.py +112 -0
- aurelian/utils/search_utils.py +95 -0
- aurelian-0.3.2.dist-info/LICENSE +22 -0
- aurelian-0.3.2.dist-info/METADATA +105 -0
- aurelian-0.3.2.dist-info/RECORD +254 -0
- aurelian-0.3.2.dist-info/WHEEL +4 -0
- aurelian-0.3.2.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,102 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
3
|
+
import yaml
|
4
|
+
from linkml.generators import JsonSchemaGenerator
|
5
|
+
from linkml_runtime.linkml_model import SchemaDefinition
|
6
|
+
from linkml_runtime.loaders import yaml_loader
|
7
|
+
from pydantic import BaseModel
|
8
|
+
from pydantic_ai import RunContext, ModelRetry
|
9
|
+
|
10
|
+
from aurelian.agents.linkml.linkml_config import LinkMLDependencies
|
11
|
+
from aurelian.dependencies.workdir import WorkDir, HasWorkdir
|
12
|
+
|
13
|
+
|
14
|
+
class LinkMLError(ModelRetry):
|
15
|
+
pass
|
16
|
+
|
17
|
+
class SchemaValidationError(LinkMLError):
|
18
|
+
"""Base exception for all schema validation errors."""
|
19
|
+
def __init__(self, message="Schema validation failed", details=None):
|
20
|
+
self.details = details or {}
|
21
|
+
super().__init__(message)
|
22
|
+
|
23
|
+
|
24
|
+
class ValidationResult(BaseModel):
|
25
|
+
valid: bool
|
26
|
+
info_messages: Optional[list[str]] = None
|
27
|
+
|
28
|
+
|
29
|
+
async def validate_then_save_schema(ctx: RunContext[HasWorkdir], schema_as_str: str, save_to_file: str= "schema.yaml") -> ValidationResult:
|
30
|
+
"""
|
31
|
+
Validate a LinkML schema.
|
32
|
+
|
33
|
+
Args:
|
34
|
+
ctx: context
|
35
|
+
schema_as_str: linkml schema (as yaml) to validate. Do not truncate, always pass the whole schema.
|
36
|
+
save_to_file: file name to save the schema to. Defaults to schema.yaml
|
37
|
+
|
38
|
+
Returns:
|
39
|
+
|
40
|
+
"""
|
41
|
+
print(f"Validating schema: {schema_as_str}")
|
42
|
+
msgs = []
|
43
|
+
try:
|
44
|
+
schema_dict = yaml.safe_load(schema_as_str)
|
45
|
+
print("YAML is valid")
|
46
|
+
except Exception as e:
|
47
|
+
raise SchemaValidationError(f"Schema is not valid yaml: {e}")
|
48
|
+
if "id" not in schema_dict:
|
49
|
+
raise SchemaValidationError("Schema does not have a top level id")
|
50
|
+
if "name" not in schema_dict:
|
51
|
+
raise SchemaValidationError("Schema does not have a top level name")
|
52
|
+
try:
|
53
|
+
schema_obj = yaml_loader.loads(schema_as_str, target_class=SchemaDefinition)
|
54
|
+
except Exception as e:
|
55
|
+
raise ModelRetry(f"Schema does not validate: {e} // {schema_as_str}")
|
56
|
+
try:
|
57
|
+
gen = JsonSchemaGenerator(schema_obj)
|
58
|
+
gen.serialize()
|
59
|
+
except Exception as e:
|
60
|
+
raise ModelRetry(f"Schema does not convert to JSON-Schema: {e} // {schema_as_str}")
|
61
|
+
try:
|
62
|
+
if save_to_file and schema_as_str:
|
63
|
+
msgs.append(f"Writing schema to {save_to_file}")
|
64
|
+
workdir = ctx.deps.workdir
|
65
|
+
workdir.write_file(save_to_file, schema_as_str)
|
66
|
+
except Exception as e:
|
67
|
+
raise ModelRetry(f"Schema does not validate: {e} // {schema_as_str}")
|
68
|
+
return ValidationResult(valid=True, info_messages=msgs)
|
69
|
+
|
70
|
+
|
71
|
+
async def validate_data(ctx: RunContext[LinkMLDependencies], schema: str, data_file: str) -> str:
|
72
|
+
"""
|
73
|
+
Validate data file against a schema.
|
74
|
+
|
75
|
+
This assumes the data file is present in the working directory.
|
76
|
+
You can write data to the working directory using the `write_to_file` tool.
|
77
|
+
|
78
|
+
Args:
|
79
|
+
ctx:
|
80
|
+
schema: the schema (as a YAML string)
|
81
|
+
data_file: the name of the data file in the working directory
|
82
|
+
|
83
|
+
Returns:
|
84
|
+
|
85
|
+
"""
|
86
|
+
print(f"Validating data file: {data_file} using schema: {schema}")
|
87
|
+
try:
|
88
|
+
schema = yaml_loader.loads(schema, target_class=SchemaDefinition)
|
89
|
+
except Exception as e:
|
90
|
+
return f"Schema does not validate: {e}"
|
91
|
+
try:
|
92
|
+
from linkml.validator import validate
|
93
|
+
instances = ctx.deps.parse_objects_from_file(data_file)
|
94
|
+
for instance in instances:
|
95
|
+
print(f"Validating {instance}")
|
96
|
+
rpt = validate(instance, schema)
|
97
|
+
print(f"Validation report: {rpt}")
|
98
|
+
if rpt.results:
|
99
|
+
return f"Data does not validate:\n{rpt.results}"
|
100
|
+
return f"{len(instances)} instances all validate successfully"
|
101
|
+
except Exception as e:
|
102
|
+
raise ModelRetry(f"Data does not validate: {e}")
|
@@ -0,0 +1,55 @@
|
|
1
|
+
"""
|
2
|
+
Agent for working with scientific literature and publications.
|
3
|
+
"""
|
4
|
+
from aurelian.agents.literature.literature_config import LiteratureDependencies
|
5
|
+
from aurelian.agents.literature.literature_tools import (
|
6
|
+
lookup_pmid,
|
7
|
+
lookup_doi,
|
8
|
+
convert_pmid_to_doi,
|
9
|
+
convert_doi_to_pmid,
|
10
|
+
get_article_abstract,
|
11
|
+
extract_text_from_pdf_url,
|
12
|
+
search_literature_web,
|
13
|
+
retrieve_literature_page
|
14
|
+
)
|
15
|
+
from aurelian.agents.filesystem.filesystem_tools import inspect_file, list_files
|
16
|
+
from pydantic_ai import Agent, Tool
|
17
|
+
|
18
|
+
SYSTEM = """
|
19
|
+
You are an expert scientific literature assistant that helps users access and analyze scientific publications.
|
20
|
+
|
21
|
+
You can help with:
|
22
|
+
- Finding and retrieving full text of articles using PubMed IDs or DOIs
|
23
|
+
- Converting between PubMed IDs and DOIs
|
24
|
+
- Extracting text from PDF articles
|
25
|
+
- Searching for scientific literature on specific topics
|
26
|
+
- Analyzing and summarizing scientific papers
|
27
|
+
|
28
|
+
Always provide accurate citations for any scientific information, including:
|
29
|
+
- Article titles
|
30
|
+
- Authors
|
31
|
+
- Journal names
|
32
|
+
- Publication dates
|
33
|
+
- DOIs and/or PubMed IDs
|
34
|
+
|
35
|
+
When quoting or referencing a specific part of a paper, always indicate which section it comes from
|
36
|
+
(e.g., abstract, methods, results, discussion).
|
37
|
+
"""
|
38
|
+
|
39
|
+
literature_agent = Agent(
|
40
|
+
model="openai:gpt-4o",
|
41
|
+
deps_type=LiteratureDependencies,
|
42
|
+
system_prompt=SYSTEM,
|
43
|
+
tools=[
|
44
|
+
Tool(lookup_pmid),
|
45
|
+
Tool(lookup_doi),
|
46
|
+
Tool(convert_pmid_to_doi),
|
47
|
+
Tool(convert_doi_to_pmid),
|
48
|
+
Tool(get_article_abstract),
|
49
|
+
Tool(extract_text_from_pdf_url),
|
50
|
+
Tool(search_literature_web),
|
51
|
+
Tool(retrieve_literature_page),
|
52
|
+
Tool(inspect_file),
|
53
|
+
Tool(list_files),
|
54
|
+
]
|
55
|
+
)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
"""
|
2
|
+
Configuration classes for the literature agent.
|
3
|
+
"""
|
4
|
+
from dataclasses import dataclass
|
5
|
+
import os
|
6
|
+
from typing import Optional
|
7
|
+
|
8
|
+
from aurelian.dependencies.workdir import HasWorkdir, WorkDir
|
9
|
+
|
10
|
+
|
11
|
+
@dataclass
|
12
|
+
class LiteratureDependencies(HasWorkdir):
|
13
|
+
"""
|
14
|
+
Configuration for the literature agent.
|
15
|
+
"""
|
16
|
+
max_results: int = 10
|
17
|
+
|
18
|
+
def __post_init__(self):
|
19
|
+
"""Initialize the config with default values."""
|
20
|
+
# Initialize workdir if not provided
|
21
|
+
if self.workdir is None:
|
22
|
+
self.workdir = WorkDir()
|
23
|
+
|
24
|
+
|
25
|
+
def get_config() -> LiteratureDependencies:
|
26
|
+
"""
|
27
|
+
Get the Literature agent configuration from environment variables or defaults.
|
28
|
+
|
29
|
+
Returns:
|
30
|
+
LiteratureDependencies: The literature dependencies
|
31
|
+
"""
|
32
|
+
workdir_path = os.environ.get("AURELIAN_WORKDIR", None)
|
33
|
+
workdir = WorkDir(location=workdir_path) if workdir_path else None
|
34
|
+
|
35
|
+
return LiteratureDependencies(workdir=workdir)
|
@@ -0,0 +1,52 @@
|
|
1
|
+
"""
|
2
|
+
Gradio UI for the literature agent.
|
3
|
+
"""
|
4
|
+
from typing import List, Optional
|
5
|
+
|
6
|
+
import gradio as gr
|
7
|
+
|
8
|
+
from aurelian.agents.literature.literature_agent import literature_agent
|
9
|
+
from aurelian.agents.literature.literature_config import LiteratureDependencies
|
10
|
+
from aurelian.utils.async_utils import run_sync
|
11
|
+
|
12
|
+
|
13
|
+
def chat(deps: Optional[LiteratureDependencies] = None, workdir: str = None, **kwargs):
|
14
|
+
"""
|
15
|
+
Initialize a chat interface for the literature agent.
|
16
|
+
|
17
|
+
Args:
|
18
|
+
deps: Optional dependencies configuration
|
19
|
+
workdir: Optional working directory path
|
20
|
+
**kwargs: Additional arguments to pass to the agent
|
21
|
+
|
22
|
+
Returns:
|
23
|
+
A Gradio chat interface
|
24
|
+
"""
|
25
|
+
if deps is None:
|
26
|
+
deps = LiteratureDependencies()
|
27
|
+
|
28
|
+
if workdir:
|
29
|
+
deps.workdir.location = workdir
|
30
|
+
|
31
|
+
def get_info(query: str, history: List[str]) -> str:
|
32
|
+
print(f"QUERY: {query}")
|
33
|
+
print(f"HISTORY: {history}")
|
34
|
+
if history:
|
35
|
+
query += "## History"
|
36
|
+
for h in history:
|
37
|
+
query += f"\n{h}"
|
38
|
+
result = run_sync(lambda: literature_agent.run_sync(query, deps=deps, **kwargs))
|
39
|
+
return result.data
|
40
|
+
|
41
|
+
return gr.ChatInterface(
|
42
|
+
fn=get_info,
|
43
|
+
type="messages",
|
44
|
+
title="Scientific Literature Assistant",
|
45
|
+
examples=[
|
46
|
+
["Look up this article: PMID:31653696"],
|
47
|
+
["Find information about Alzheimer's disease genetics in recent papers"],
|
48
|
+
["What is the DOI for PMID:27629041?"],
|
49
|
+
["Get the abstract of PMID:30478089"],
|
50
|
+
["Convert this DOI to a PMID: 10.1038/nature12373"]
|
51
|
+
]
|
52
|
+
)
|
@@ -0,0 +1,174 @@
|
|
1
|
+
"""
|
2
|
+
MCP tools for working with scientific literature and publications.
|
3
|
+
"""
|
4
|
+
import os
|
5
|
+
from typing import Optional
|
6
|
+
|
7
|
+
from mcp.server.fastmcp import FastMCP
|
8
|
+
|
9
|
+
import aurelian.agents.literature.literature_tools as lt
|
10
|
+
import aurelian.agents.filesystem.filesystem_tools as fst
|
11
|
+
from aurelian.agents.literature.literature_agent import SYSTEM
|
12
|
+
from aurelian.agents.literature.literature_config import LiteratureDependencies
|
13
|
+
from pydantic_ai import RunContext
|
14
|
+
|
15
|
+
# Initialize FastMCP server
|
16
|
+
mcp = FastMCP("literature", instructions=SYSTEM)
|
17
|
+
|
18
|
+
|
19
|
+
from aurelian.dependencies.workdir import WorkDir
|
20
|
+
|
21
|
+
def deps() -> LiteratureDependencies:
|
22
|
+
deps = LiteratureDependencies()
|
23
|
+
# Set the location from environment variable or default
|
24
|
+
loc = os.getenv("AURELIAN_WORKDIR", "/tmp/aurelian")
|
25
|
+
deps.workdir = WorkDir(loc)
|
26
|
+
return deps
|
27
|
+
|
28
|
+
def ctx() -> RunContext[LiteratureDependencies]:
|
29
|
+
rc: RunContext[LiteratureDependencies] = RunContext[LiteratureDependencies](
|
30
|
+
deps=deps(),
|
31
|
+
model=None, usage=None, prompt=None,
|
32
|
+
)
|
33
|
+
return rc
|
34
|
+
|
35
|
+
|
36
|
+
@mcp.tool()
|
37
|
+
async def lookup_pmid(pmid: str) -> str:
|
38
|
+
"""
|
39
|
+
Lookup the text of a PubMed article by its PMID.
|
40
|
+
|
41
|
+
Args:
|
42
|
+
pmid: The PubMed ID to look up (format: "PMID:nnnnnnn")
|
43
|
+
|
44
|
+
Returns:
|
45
|
+
Full text if available, otherwise abstract
|
46
|
+
"""
|
47
|
+
return await lt.lookup_pmid(ctx(), pmid)
|
48
|
+
|
49
|
+
@mcp.tool()
|
50
|
+
async def lookup_doi(doi: str) -> str:
|
51
|
+
"""
|
52
|
+
Lookup the text of an article by its DOI.
|
53
|
+
|
54
|
+
Args:
|
55
|
+
doi: The DOI to look up
|
56
|
+
|
57
|
+
Returns:
|
58
|
+
Full text if available, otherwise abstract
|
59
|
+
"""
|
60
|
+
return await lt.lookup_doi(doi)
|
61
|
+
|
62
|
+
|
63
|
+
@mcp.tool()
|
64
|
+
async def convert_pmid_to_doi(pmid: str) -> str:
|
65
|
+
"""
|
66
|
+
Convert a PubMed ID (PMID) to a DOI.
|
67
|
+
|
68
|
+
Args:
|
69
|
+
pmid: The PubMed ID to convert
|
70
|
+
|
71
|
+
Returns:
|
72
|
+
The corresponding DOI
|
73
|
+
"""
|
74
|
+
return await lt.convert_pmid_to_doi(pmid)
|
75
|
+
|
76
|
+
|
77
|
+
@mcp.tool()
|
78
|
+
async def convert_doi_to_pmid(doi: str) -> str:
|
79
|
+
"""
|
80
|
+
Convert a DOI to a PubMed ID (PMID).
|
81
|
+
|
82
|
+
Args:
|
83
|
+
doi: The DOI to convert
|
84
|
+
|
85
|
+
Returns:
|
86
|
+
The corresponding PubMed ID
|
87
|
+
"""
|
88
|
+
return await lt.convert_doi_to_pmid(doi)
|
89
|
+
|
90
|
+
|
91
|
+
@mcp.tool()
|
92
|
+
async def get_article_abstract(identifier: str) -> str:
|
93
|
+
"""
|
94
|
+
Get the abstract of an article by its PMID or DOI.
|
95
|
+
|
96
|
+
Args:
|
97
|
+
identifier: PMID or DOI of the article
|
98
|
+
|
99
|
+
Returns:
|
100
|
+
The article abstract
|
101
|
+
"""
|
102
|
+
return await lt.get_article_abstract(identifier)
|
103
|
+
|
104
|
+
|
105
|
+
@mcp.tool()
|
106
|
+
async def extract_text_from_pdf_url(url: str) -> str:
|
107
|
+
"""
|
108
|
+
Extract text from a PDF at the given URL.
|
109
|
+
|
110
|
+
Args:
|
111
|
+
url: URL to the PDF file
|
112
|
+
|
113
|
+
Returns:
|
114
|
+
Extracted text from the PDF
|
115
|
+
"""
|
116
|
+
return await lt.extract_text_from_pdf_url(url)
|
117
|
+
|
118
|
+
|
119
|
+
@mcp.tool()
|
120
|
+
async def search_literature_web(query: str) -> str:
|
121
|
+
"""
|
122
|
+
Search the web for scientific literature.
|
123
|
+
|
124
|
+
Args:
|
125
|
+
query: The search query
|
126
|
+
|
127
|
+
Returns:
|
128
|
+
Search results with summaries
|
129
|
+
"""
|
130
|
+
return await lt.search_literature_web(query)
|
131
|
+
|
132
|
+
|
133
|
+
@mcp.tool()
|
134
|
+
async def retrieve_literature_page(url: str) -> str:
|
135
|
+
"""
|
136
|
+
Fetch the contents of a web page related to scientific literature.
|
137
|
+
|
138
|
+
Args:
|
139
|
+
url: The URL to fetch
|
140
|
+
|
141
|
+
Returns:
|
142
|
+
The contents of the web page
|
143
|
+
"""
|
144
|
+
return await lt.retrieve_literature_page(url)
|
145
|
+
|
146
|
+
|
147
|
+
@mcp.tool()
|
148
|
+
async def inspect_file(data_file: str) -> str:
|
149
|
+
"""
|
150
|
+
Inspect a file in the working directory.
|
151
|
+
|
152
|
+
Args:
|
153
|
+
data_file: name of file
|
154
|
+
|
155
|
+
Returns:
|
156
|
+
Contents of the file
|
157
|
+
"""
|
158
|
+
return await fst.inspect_file(ctx(), data_file)
|
159
|
+
|
160
|
+
|
161
|
+
@mcp.tool()
|
162
|
+
async def list_files() -> str:
|
163
|
+
"""
|
164
|
+
List files in the working directory.
|
165
|
+
|
166
|
+
Returns:
|
167
|
+
List of files in the working directory
|
168
|
+
"""
|
169
|
+
return await fst.list_files(ctx())
|
170
|
+
|
171
|
+
|
172
|
+
if __name__ == "__main__":
|
173
|
+
# Initialize and run the server
|
174
|
+
mcp.run(transport='stdio')
|
@@ -0,0 +1,182 @@
|
|
1
|
+
"""
|
2
|
+
Tools for the literature agent.
|
3
|
+
"""
|
4
|
+
from typing import Optional, List, Dict
|
5
|
+
|
6
|
+
from pydantic_ai import RunContext, ModelRetry
|
7
|
+
|
8
|
+
from aurelian.agents.literature.literature_config import LiteratureDependencies
|
9
|
+
from aurelian.utils.doi_fetcher import DOIFetcher
|
10
|
+
from aurelian.utils.pubmed_utils import (
|
11
|
+
get_pmid_text,
|
12
|
+
get_doi_text,
|
13
|
+
pmid_to_doi,
|
14
|
+
doi_to_pmid,
|
15
|
+
get_abstract_from_pubmed,
|
16
|
+
)
|
17
|
+
from aurelian.utils.pdf_fetcher import extract_text_from_pdf
|
18
|
+
from aurelian.utils.search_utils import web_search, retrieve_web_page
|
19
|
+
|
20
|
+
|
21
|
+
async def lookup_pmid(pmid: str) -> str:
|
22
|
+
"""
|
23
|
+
Lookup the text of a PubMed article by its PMID.
|
24
|
+
|
25
|
+
A PMID should be of the form "PMID:nnnnnnn" (no underscores).
|
26
|
+
|
27
|
+
Args:
|
28
|
+
pmid: The PubMed ID to look up
|
29
|
+
|
30
|
+
Returns:
|
31
|
+
str: Full text if available, otherwise abstract
|
32
|
+
"""
|
33
|
+
print(f"LOOKUP PMID: {pmid}")
|
34
|
+
try:
|
35
|
+
result = get_pmid_text(pmid)
|
36
|
+
print(f"RESULT LENGTH: {len(result)} // {result[:100]}")
|
37
|
+
if not result or "Error" in result:
|
38
|
+
raise ModelRetry(f"Could not retrieve text for PMID: {pmid}. Try using the abstract only or a different identifier.")
|
39
|
+
return result
|
40
|
+
except Exception as e:
|
41
|
+
raise ModelRetry(f"Error retrieving PMID {pmid}: {str(e)}. Try using the abstract only or a different identifier.")
|
42
|
+
|
43
|
+
|
44
|
+
async def lookup_doi(doi: str) -> str:
|
45
|
+
"""
|
46
|
+
Lookup the text of an article by its DOI.
|
47
|
+
|
48
|
+
Args:
|
49
|
+
doi: The DOI to look up (e.g., "10.1038/nature12373")
|
50
|
+
|
51
|
+
Returns:
|
52
|
+
str: Full text if available, otherwise abstract
|
53
|
+
"""
|
54
|
+
print(f"LOOKUP DOI: {doi}")
|
55
|
+
try:
|
56
|
+
result = get_doi_text(doi)
|
57
|
+
if not result or "Error" in result or "not available" in result.lower():
|
58
|
+
raise ModelRetry(f"Could not retrieve text for DOI: {doi}. Try using a PubMed ID or a different approach.")
|
59
|
+
return result
|
60
|
+
except Exception as e:
|
61
|
+
raise ModelRetry(f"Error retrieving DOI {doi}: {str(e)}. Try using a PubMed ID or a different approach.")
|
62
|
+
|
63
|
+
|
64
|
+
async def convert_pmid_to_doi(pmid: str) -> Optional[str]:
|
65
|
+
"""
|
66
|
+
Convert a PubMed ID to a DOI.
|
67
|
+
|
68
|
+
Args:
|
69
|
+
pmid: The PubMed ID to convert
|
70
|
+
|
71
|
+
Returns:
|
72
|
+
str: The corresponding DOI, or None if not found
|
73
|
+
"""
|
74
|
+
print(f"CONVERT PMID TO DOI: {pmid}")
|
75
|
+
try:
|
76
|
+
result = pmid_to_doi(pmid)
|
77
|
+
if not result:
|
78
|
+
raise ModelRetry(f"Could not convert PMID {pmid} to DOI. This article may not have a DOI assigned.")
|
79
|
+
return result
|
80
|
+
except Exception as e:
|
81
|
+
raise ModelRetry(f"Error converting PMID {pmid} to DOI: {str(e)}")
|
82
|
+
|
83
|
+
|
84
|
+
async def convert_doi_to_pmid(doi: str) -> Optional[str]:
|
85
|
+
"""
|
86
|
+
Convert a DOI to a PubMed ID.
|
87
|
+
|
88
|
+
Args:
|
89
|
+
doi: The DOI to convert
|
90
|
+
|
91
|
+
Returns:
|
92
|
+
str: The corresponding PubMed ID, or None if not found
|
93
|
+
"""
|
94
|
+
print(f"CONVERT DOI TO PMID: {doi}")
|
95
|
+
try:
|
96
|
+
result = doi_to_pmid(doi)
|
97
|
+
if not result:
|
98
|
+
raise ModelRetry(f"Could not convert DOI {doi} to PMID. This article may not be indexed in PubMed.")
|
99
|
+
return result
|
100
|
+
except Exception as e:
|
101
|
+
raise ModelRetry(f"Error converting DOI {doi} to PMID: {str(e)}")
|
102
|
+
|
103
|
+
|
104
|
+
async def get_article_abstract(pmid: str) -> str:
|
105
|
+
"""
|
106
|
+
Get only the abstract of an article by its PubMed ID.
|
107
|
+
|
108
|
+
Args:
|
109
|
+
pmid: The PubMed ID to look up
|
110
|
+
|
111
|
+
Returns:
|
112
|
+
str: The article abstract
|
113
|
+
"""
|
114
|
+
print(f"GET ABSTRACT: {pmid}")
|
115
|
+
try:
|
116
|
+
result = get_abstract_from_pubmed(pmid)
|
117
|
+
if not result or result.endswith("No abstract available"):
|
118
|
+
raise ModelRetry(f"No abstract available for PMID {pmid}. This article may not have an abstract or the PMID may be incorrect.")
|
119
|
+
return result
|
120
|
+
except Exception as e:
|
121
|
+
raise ModelRetry(f"Error retrieving abstract for PMID {pmid}: {str(e)}")
|
122
|
+
|
123
|
+
|
124
|
+
async def extract_text_from_pdf_url(ctx: RunContext[LiteratureDependencies], pdf_url: str) -> str:
|
125
|
+
"""
|
126
|
+
Extract text from a PDF at the given URL.
|
127
|
+
|
128
|
+
Args:
|
129
|
+
ctx: The run context
|
130
|
+
pdf_url: URL to the PDF file
|
131
|
+
|
132
|
+
Returns:
|
133
|
+
str: The extracted text content
|
134
|
+
"""
|
135
|
+
print(f"EXTRACT PDF: {pdf_url}")
|
136
|
+
try:
|
137
|
+
result = extract_text_from_pdf(pdf_url)
|
138
|
+
if not result or "Error" in result:
|
139
|
+
raise ModelRetry(f"Could not extract text from PDF at {pdf_url}. The URL may be invalid or the PDF may be password-protected.")
|
140
|
+
return result
|
141
|
+
except Exception as e:
|
142
|
+
raise ModelRetry(f"Error extracting text from PDF {pdf_url}: {str(e)}")
|
143
|
+
|
144
|
+
|
145
|
+
async def search_literature_web(query: str) -> str:
|
146
|
+
"""
|
147
|
+
Search the web for scientific literature using a text query.
|
148
|
+
|
149
|
+
Args:
|
150
|
+
query: The search query (e.g., "alzheimer's disease genetics 2023")
|
151
|
+
|
152
|
+
Returns:
|
153
|
+
str: Search results with summaries
|
154
|
+
"""
|
155
|
+
print(f"LITERATURE WEB SEARCH: {query}")
|
156
|
+
try:
|
157
|
+
result = web_search(query)
|
158
|
+
if not result:
|
159
|
+
raise ModelRetry(f"No search results found for query: {query}. Try using different keywords.")
|
160
|
+
return result
|
161
|
+
except Exception as e:
|
162
|
+
raise ModelRetry(f"Error searching the web for '{query}': {str(e)}")
|
163
|
+
|
164
|
+
|
165
|
+
async def retrieve_literature_page(url: str) -> str:
|
166
|
+
"""
|
167
|
+
Fetch the contents of a literature webpage.
|
168
|
+
|
169
|
+
Args:
|
170
|
+
url: The URL to fetch
|
171
|
+
|
172
|
+
Returns:
|
173
|
+
str: The contents of the webpage
|
174
|
+
"""
|
175
|
+
print(f"FETCH LITERATURE URL: {url}")
|
176
|
+
try:
|
177
|
+
result = retrieve_web_page(url)
|
178
|
+
if not result or len(result.strip()) < 20:
|
179
|
+
raise ModelRetry(f"Could not retrieve meaningful content from {url}. The URL may be invalid or require authentication.")
|
180
|
+
return result
|
181
|
+
except Exception as e:
|
182
|
+
raise ModelRetry(f"Error retrieving webpage {url}: {str(e)}")
|
@@ -0,0 +1,25 @@
|
|
1
|
+
"""
|
2
|
+
Monarch agent package for interacting with the Monarch Knowledge Base.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from .monarch_agent import monarch_agent, MONARCH_SYSTEM_PROMPT
|
6
|
+
from .monarch_config import MonarchDependencies, get_config
|
7
|
+
from .monarch_gradio import chat
|
8
|
+
from .monarch_tools import find_gene_associations, find_disease_associations
|
9
|
+
|
10
|
+
__all__ = [
|
11
|
+
# Agent
|
12
|
+
"monarch_agent",
|
13
|
+
"MONARCH_SYSTEM_PROMPT",
|
14
|
+
|
15
|
+
# Config
|
16
|
+
"MonarchDependencies",
|
17
|
+
"get_config",
|
18
|
+
|
19
|
+
# Tools
|
20
|
+
"find_gene_associations",
|
21
|
+
"find_disease_associations",
|
22
|
+
|
23
|
+
# Gradio
|
24
|
+
"chat",
|
25
|
+
]
|
@@ -0,0 +1,44 @@
|
|
1
|
+
"""
|
2
|
+
Agent for interacting with the Monarch knowledge base.
|
3
|
+
"""
|
4
|
+
from pydantic_ai import Agent
|
5
|
+
|
6
|
+
from .monarch_config import MonarchDependencies, get_config
|
7
|
+
from .monarch_tools import find_gene_associations, find_disease_associations
|
8
|
+
|
9
|
+
# System prompt for the Monarch agent
|
10
|
+
MONARCH_SYSTEM_PROMPT = """
|
11
|
+
You are a helpful assistant specializing in biomedical data from the Monarch Knowledge Base.
|
12
|
+
You can help researchers find relationships between genes, diseases, phenotypes, and other biomedical entities.
|
13
|
+
|
14
|
+
The Monarch Knowledge Base integrates data from multiple biomedical databases and provides a unified interface
|
15
|
+
for querying associations between different biological entities.
|
16
|
+
|
17
|
+
You can:
|
18
|
+
- Find associations for genes, including what diseases they're linked to
|
19
|
+
- Find associations for diseases, including what genes and phenotypes they're linked to
|
20
|
+
- Provide information about biological relationships in a structured way
|
21
|
+
|
22
|
+
When working with identifiers:
|
23
|
+
- Gene symbols should be specified as HGNC or MGI symbols (e.g. "BRCA1")
|
24
|
+
- Disease IDs can be specified as MONDO, OMIM, or Orphanet IDs (e.g. "MONDO:0007254")
|
25
|
+
- Phenotype IDs can be specified as HP terms (e.g. "HP:0000118")
|
26
|
+
|
27
|
+
Present your findings in a clear, organized manner that helps researchers understand the biological significance
|
28
|
+
of the associations. Include relevant details about:
|
29
|
+
- Source of the associations
|
30
|
+
- Strength of evidence
|
31
|
+
- Type of relationship (causal, correlative, etc.)
|
32
|
+
- Relevant literature references when available
|
33
|
+
"""
|
34
|
+
|
35
|
+
# Create the agent with the system prompt
|
36
|
+
monarch_agent = Agent(
|
37
|
+
model="openai:gpt-4o",
|
38
|
+
system_prompt=MONARCH_SYSTEM_PROMPT,
|
39
|
+
deps_type=MonarchDependencies,
|
40
|
+
)
|
41
|
+
|
42
|
+
# Register the tools with the agent
|
43
|
+
monarch_agent.tool(find_gene_associations)
|
44
|
+
monarch_agent.tool(find_disease_associations)
|