Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
a3a1660
fix test error
yjzhang Apr 29, 2026
9252844
Merge branch 'NCATSTranslator:main' into main
yjzhang Apr 30, 2026
afffeee
adding scoring to pathfinder output
yjzhang Apr 30, 2026
43df311
add nodes to TCT_pathfinder
yjzhang May 1, 2026
384c56a
start on neighborhood finder
yjzhang May 1, 2026
d712fe3
neighborhood finder - new output type
yjzhang May 1, 2026
3b097a9
removed extraneous function
yjzhang May 1, 2026
2ffe87d
consolidated some duplicate functions
yjzhang May 1, 2026
e6ff218
Merge branch 'NCATSTranslator:main' into main
yjzhang May 1, 2026
2b8d4dd
renamed file
yjzhang May 7, 2026
85e0fa3
Merge branch 'NCATSTranslator:main' into main
yjzhang May 14, 2026
83261b0
add attribute constraints to translator_query.build_query_json
yjzhang May 18, 2026
856fdb1
merged
yjzhang May 18, 2026
031bd2f
Merge branch 'NCATSTranslator:main' into main
yjzhang May 19, 2026
f135e79
testing queries with constraints
yjzhang May 21, 2026
f1de54a
notebook for queries with constraints
yjzhang May 22, 2026
c188e40
neighborhood finder with constraints
yjzhang May 23, 2026
cff0467
update docs to include neighborhood/pathfinder
yjzhang May 28, 2026
0c00041
notebook
yjzhang May 28, 2026
4a87c87
queries with constraints, updating docs
yjzhang May 29, 2026
715ab9f
Merge branch 'NCATSTranslator:main' into main
yjzhang Jun 1, 2026
1c4727f
fixed some potential bugs with neighborhood finder
yjzhang Jun 2, 2026
30ab2f4
Merge branch 'main' of https://github.com/yjzhang/Translator_componen…
yjzhang Jun 2, 2026
471c8a0
working on updating the metakg query...
yjzhang Jun 2, 2026
ce5e739
working on updating the metakg query...
yjzhang Jun 2, 2026
33114e6
add new metakg api url
yjzhang Jun 2, 2026
f8ea041
metakg stuff
yjzhang Jun 2, 2026
3cb26e8
using new metaKG URL
yjzhang Jun 3, 2026
3be3ad1
remove json files from codespell checks
yjzhang Jun 3, 2026
eac06e5
merged
yjzhang Jun 4, 2026
7f2ca92
merged
yjzhang Jun 4, 2026
1cc8355
Merge branch 'NCATSTranslator:main' into main
yjzhang Jun 8, 2026
62f2ba3
Merge branch 'NCATSTranslator:main' into main
yjzhang Jun 11, 2026
eed4b91
neighborhood finder with multiple input nodes
yjzhang Jun 19, 2026
cd3aa5c
Merge branch 'NCATSTranslator:main' into main
yjzhang Jun 25, 2026
f94ac70
Merge branch 'NCATSTranslator:main' into main
yjzhang Jul 7, 2026
65cb46b
Merge branch 'NCATSTranslator:main' into main
yjzhang Jul 7, 2026
7dffc25
Merge branch 'NCATSTranslator:main' into main
yjzhang Jul 7, 2026
18ab874
remove certain requirements
yjzhang Jul 7, 2026
b07e6fb
restore requirements
yjzhang Jul 7, 2026
f65f805
fix typo, fix docs
yjzhang Jul 7, 2026
4526777
fix typos
yjzhang Jul 7, 2026
f1a6632
move ipycytoscape imports to inside function
yjzhang Jul 7, 2026
9570fc0
fix docstring formatting
yjzhang Jul 7, 2026
ead1cd1
Merge branch 'NCATSTranslator:main' into main
yjzhang Jul 7, 2026
5307006
updated docstrings
yjzhang Jul 7, 2026
414dccf
update docstrings
yjzhang Jul 7, 2026
a5d9086
docstring examples
yjzhang Jul 7, 2026
59e06fe
Merge branch 'NCATSTranslator:main' into main
yjzhang Jul 7, 2026
3da2a9f
Merge branch 'NCATSTranslator:main' into main
yjzhang Jul 15, 2026
e016745
Merge branch 'NCATSTranslator:main' into main
yjzhang Aug 11, 2026
c5cab50
Merge branch 'NCATSTranslator:main' into main
yjzhang Aug 14, 2026
7be7e39
Merge branch 'NCATSTranslator:main' into main
yjzhang Aug 17, 2026
b9317c7
Merge branch 'NCATSTranslator:main' into main
yjzhang Aug 19, 2026
f3a3cba
add more documentation/type annotations
yjzhang Aug 20, 2026
3fb426f
add some additional documentation
yjzhang Aug 21, 2026
e92f256
Merge branch 'NCATSTranslator:main' into main
yjzhang Aug 21, 2026
7adf3c9
changed name in server test
yjzhang Aug 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 25 additions & 8 deletions TCT/TCT_neighborhood_finder.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# reviewed by yjzhang, 2026-08-19
from collections import Counter

import pandas as pd
Expand All @@ -18,12 +19,29 @@
from .TCT_pathfinder import generate_score_results, build_query_graph

def parse_results_for_neighborhood_finder(start_node_id:str, results:dict,
start_node_categories=None, end_node_categories=None,
get_node_info=True,
scoring_method='infores'):
start_node_categories:list|None=None, end_node_categories:list|None=None,
get_node_info:bool=True,
scoring_method:str='infores') -> dict:
"""
Converts the results of two TRAPI queries into the same general json format as the other pathfinder APIs.
scoring_method is how the node scores are generated, and could be 'infores' or 'edges'.

Params
------
start_node_id : str
A CURIE id
results : dict
Results of a TRAPI query (e.g. parallel_api_query)
start_node_categories : list | None
Categories for the starting node.
end_node_categories : list | None
Categories for the ending nodes of the query.
get_node_info : bool
scoring_method : str
scoring_method is how the node scores are generated, and could be 'infores' or 'edges'. Default: 'infores'

Returns
-------
A dict of the format `{'query_graph': ..., 'knowledge_graph': ..., 'results':, 'auxiliary_graphs':...}`
"""
# nodes
node_info = {}
Expand Down Expand Up @@ -108,11 +126,10 @@ def parse_results_for_neighborhood_finder(start_node_id:str, results:dict,
return output


# TODO: can we use multiple input nodes?
def parse_results_for_neighborhood_finder_multiple_inputs(start_node_ids:list[str], results:dict,
start_node_categories=None, end_node_categories=None,
get_node_info=True,
scoring_method='infores'):
start_node_categories:list|None=None, end_node_categories:list|None=None,
get_node_info:bool=True,
scoring_method:str='infores') -> dict:
"""
Converts the results of two TRAPI queries into the same general json format as the other pathfinder APIs.
scoring_method is how the node scores are generated, and could be 'infores' or 'edges'.
Expand Down
5 changes: 2 additions & 3 deletions TCT/TCT_network_annotator.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
from . import TCT_neighborhood_finder


def network_annotator(gene_list,
select_APIs,
def network_annotator(gene_list,
select_APIs,
node2_categories,
select_metaKG,
API_predicates,
Expand All @@ -28,7 +28,6 @@ def network_annotator(gene_list,
dict
Merged TRAPI JSON object.
"""
import json
TCT_neighborhood_finder_result = {}
for gene in gene_list:
input_identifiers = name_resolver.lookup(gene, only_taxa='NCBITaxon:9606', biolink_type='biolink:Gene').curie
Expand Down
94 changes: 82 additions & 12 deletions TCT/TCT_pathfinder.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# TCT Pathfinder...
# reviewed by yjzhang, 2026-08-20
import requests

import pandas as pd
Expand All @@ -21,15 +22,15 @@



def format_query_json_for_pathfinder_with_constraints(subject_ids,
object_ids=None,
def format_query_json_for_pathfinder_with_constraints(subject_ids:str,
object_ids:str,
subject_categories=None,
object_categories=None,
predicates=None,
constraints=None
):
) -> dict:
"""
format user's input into a query json for pathfinder pipeline with constraints on the intermediate node categories.
Format user's input into a query json for pathfinder pipeline with constraints on the intermediate node categories.

Parameters
----------
Expand Down Expand Up @@ -112,7 +113,7 @@ def format_query_json_for_pathfinder_with_constraints(subject_ids,

return q

def build_query_graph(start_node_id, end_node_id, start_node_categories=None, end_node_categories=None, constraints_path=None):
def build_query_graph(start_node_id:str, end_node_id:str, start_node_categories=None, end_node_categories=None, constraints_path=None):
"""
start_node_categories and end_node_categories are lists of categories.
"""
Expand Down Expand Up @@ -151,7 +152,7 @@ def build_query_graph(start_node_id, end_node_id, start_node_categories=None, en
return q


def generate_score_results(results, method='infores'):
def generate_score_results(results:dict, method='infores'):
"""
Generates a score dict, and a list of "analyses".
method can be 'infores' or 'edges'
Expand Down Expand Up @@ -312,9 +313,20 @@ def parse_results_for_pathfinder(start_node_id:str, end_node_id:str, result1:dic


# define a function that uses the query_json as an template and change the ids and categories of the nodes
def format_pathfinder_query(node1_id, node1_category, node2_id, node2_category):
def format_pathfinder_query(node1_id:str, node1_category:str, node2_id:str, node2_category:str) -> dict:
'''

Formats a query to the Pathfinder API.

Params
------
node1_id : str
node1_category : str
node2_id : str
node2_category : str

Returns
-------
A dict formatted as a JSON query to the Pathfinder API.
'''
query_json = {
"message": {
Expand Down Expand Up @@ -350,15 +362,44 @@ def format_pathfinder_query(node1_id, node1_category, node2_id, node2_category):
return query_json


def query_aragorn_pathfinder(node1_id, node1_category, node2_id, node2_category):
def query_aragorn_pathfinder(node1_id:str, node1_category:str, node2_id:str, node2_category:str) -> str:
"""
This queries the ARAGORN Pathfinder API.

Params
------
node1_id : str
node1_category : str
node2_id : str
node2_category : str

Returns
-------
A string (which should be a JSON) representing the result of an ARAGORN pathfinder query.
"""
#aragorn_endpoint = 'https://shepherd.renci.org/aragorn/query' # dev endpoint
aragorn_endpoint = 'https://shepherd.ci.transltr.io/aragorn/query' # new ci endpoint
query_current = format_pathfinder_query(node1_id, node1_category, node2_id, node2_category)
response = requests.post(aragorn_endpoint, json=query_current)
return response


def query_aragorn_pathfinder_with_constraints(node1_id, node1_category, node2_id, node2_category, constraints):
def query_aragorn_pathfinder_with_constraints(node1_id:str, node1_category:str, node2_id:str, node2_category:str, constraints:list) -> str:
"""
This queries the ARAGORN Pathfinder API with a list of constraints.

Params
------
node1_id : str
node1_category : str
node2_id : str
node2_category : str
constraints : list

Returns
-------
A string (which should be a JSON) representing the result of an ARAGORN pathfinder query.
"""
#aragorn_endpoint = 'https://shepherd.renci.org/aragorn/query' # dev endpoint
aragorn_endpoint = 'https://shepherd.ci.transltr.io/aragorn/query' # new ci endpoint
query_current = format_query_json_for_pathfinder_with_constraints(
Expand All @@ -371,13 +412,42 @@ def query_aragorn_pathfinder_with_constraints(node1_id, node1_category, node2_id
response = requests.post(aragorn_endpoint, json=query_current)
return response

def query_arax_pathfinder(node1_id, node1_category, node2_id, node2_category):
def query_arax_pathfinder(node1_id:str, node1_category:str, node2_id:str, node2_category:str) -> str:
"""
This queries the ARAX Pathfinder API.

Params
------
node1_id : str
node1_category : str
node2_id : str
node2_category : str

Returns
-------
A string (which should be a JSON) representing the result of an ARAX pathfinder query.
"""
ARAX_endpoint = 'https://arax.ci.transltr.io/api/arax/v1.4/query'
query_current = format_pathfinder_query(node1_id, node1_category, node2_id, node2_category)
response = requests.post(ARAX_endpoint, json=query_current)
return response

def query_arax_pathfinder_with_constraints(node1_id, node1_category, node2_id, node2_category, constraints):
def query_arax_pathfinder_with_constraints(node1_id:str, node1_category:str, node2_id:str, node2_category:str, constraints:list) -> str:
"""
This queries the ARAX Pathfinder API with a list of constraints.

Params
------
node1_id : str
node1_category : str
node2_id : str
node2_category : str
constraints : list

Returns
-------
A string (which should be a JSON) representing the result of an ARAX pathfinder query.
"""
ARAX_endpoint = 'https://arax.ci.transltr.io/api/arax/v1.4/query'
query_current = format_query_json_for_pathfinder_with_constraints(
subject_ids=node1_id,
Expand Down
13 changes: 7 additions & 6 deletions TCT/name_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

API docs: https://name-lookup.ci.transltr.io/docs
"""
# reviewed by yjzhang, 2026-08-19
import urllib.parse

import requests
Expand All @@ -12,7 +13,7 @@
URL = 'https://name-lookup.ci.transltr.io/'
"""This is the root URL for the API."""

def status():
def status() -> str:
"""
Returns the status of the Name Resolver API.
"""
Expand All @@ -21,7 +22,7 @@ def status():
return response.json()


def lookup(query: str, return_top_response:bool=True, return_synonyms:bool=False, limit:int=10, **kwargs):
def lookup(query: str, return_top_response:bool=True, return_synonyms:bool=False, limit:int=10, **kwargs) -> TranslatorNode | list[TranslatorNode]:
"""
A wrapper around the `lookup` api endpoint. Given a query string, this returns a TranslatorNode object or a list of TranslatorNode objects corresponding to the given name.

Expand Down Expand Up @@ -72,7 +73,7 @@ def lookup(query: str, return_top_response:bool=True, return_synonyms:bool=False
raise requests.RequestException('Response from server had error, code ' + str(response.status_code) + ' ' + str(response))


def synonyms(query: str|list, **kwargs):
def synonyms(query: str|list, **kwargs) -> dict[str, TranslatorNode]:
"""
A wrapper around the `synonyms` api endpoint. Given a CURIE or a list of CURIEs, this returns a dict of CURIE id : TranslatorNode for all synonyms for the given query.

Expand Down Expand Up @@ -107,15 +108,15 @@ def synonyms(query: str|list, **kwargs):
raise requests.RequestException('Response from server had error, code ' + str(response.status_code) + ' ' + str(response))


def chunk_list(data:list, size:int):
def chunk_list(data:list, size:int) -> list:
#Extra method to help chunk large files and avoid 504 error.
chunks = []
for i in range(0, len(data), size):
chunks.append(data[i: i+size])
return chunks


def batch_lookup(strings:list[str], size: int=25, return_top_response:bool=True, return_synonyms:bool=False, **kwargs) -> dict:
def batch_lookup(strings:list[str], size: int=25, return_top_response:bool=True, return_synonyms:bool=False, **kwargs) -> dict[str, TranslatorNode|list[TranslatorNode]]:
"""
A wrapper around the `bulk-lookup` api endpoint. Given a list of query strings, this returns a TranslatorNode object or a list of TranslatorNode objects corresponding to the given name.

Expand Down Expand Up @@ -174,7 +175,7 @@ def batch_lookup(strings:list[str], size: int=25, return_top_response:bool=True,
return curies


def batch_synonyms(strings:list[str], size:int=50, **kwargs) -> dict:
def batch_synonyms(strings:list[str], size:int=50, **kwargs) -> dict[str, TranslatorNode]:
"""
A wrapper around the `synonyms` API endpoint, using POST. Given a list of CURIEs, this returns a dict of CURIE:TranslatorNode, where each TranslatorNode contains all synonyms for the given CURIE.

Expand Down
3 changes: 2 additions & 1 deletion TCT/node_annotator.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

API docs: https://annotator.transltr.io/
"""
# reviewed by yjzhang, 2026-08-19
import urllib.parse

import requests
Expand All @@ -25,7 +26,7 @@ def lookup_curie(curie: str, **kwargs):
return lookup_curies([curie], **kwargs)[curie]


def lookup_curies(curies: list[str], **kwargs):
def lookup_curies(curies: list[str], **kwargs) -> dict[str, dict]:
"""
A wrapper around the `curies` API endpoint. Given a list of CURIEs, this returns a dictionary where each
CURIE is mapped to a list of annotations.
Expand Down
4 changes: 3 additions & 1 deletion TCT/node_normalizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

API docs: https://nodenorm.transltr.io/docs
"""
# reviewed by yjzhang, 2026-08-19
import urllib.parse

import requests
Expand All @@ -20,6 +21,7 @@ def status():
response.raise_for_status()
return response.json()


def get_normalized_nodes(query: str | list[str],
return_equivalent_identifiers:bool=False,
mode:str='get',
Expand Down Expand Up @@ -135,7 +137,7 @@ def get_preferred_names(id_list:list[str], batch_limit=500, **kwargs) -> dict[st
return name_map


def ID_convert_to_preferred_name_nodeNormalizer(id_list):
def ID_convert_to_preferred_name_nodeNormalizer(id_list: list[str]) -> dict[str, str]:
'''
Convert a list of CURIEs to their preferred names using NodeNorm.
Arg:
Expand Down
1 change: 1 addition & 0 deletions TCT/translator_node.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# reviewed by yjzhang, 2026-08-19
# translator graph node
from dataclasses import dataclass
import typing
Expand Down
3 changes: 2 additions & 1 deletion TCT/translator_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,13 @@ def get_translator_API_predicates() -> tuple[dict, pandas.DataFrame, dict]:
return APInames, metaKG, API_predicates


def build_attribute_constraint(attribute_id, operator, value, name=None, is_not=False):
def build_attribute_constraint(attribute_id:str, operator:str, value, name=None, is_not=False):
"""
This creates an attribute constraint for a TRAPI query dict.

Example
-------

"""
if name is None:
name = ''
Expand Down
6 changes: 3 additions & 3 deletions tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
def test_mcp_server_exists():
"""Test that MCP server instance exists and has correct name."""
assert mcp is not None
assert mcp.name == "translator-toolkit"
assert mcp.name == "TCT"


def test_mcp_server_ready():
"""Test that MCP server is ready for orchestrating agent access."""
# Check that the server has the FastMCP functionality needed for agents
assert hasattr(mcp, 'run'), "MCP server should be runnable for agents"
assert mcp.name == "translator-toolkit", "MCP server should have correct name for agents"
assert mcp.name == "TCT", "MCP server should have correct name for agents"


def test_mcp_tools_accessible():
Expand All @@ -27,4 +27,4 @@ def test_mcp_tools_accessible():

# These should exist as tool objects that agents can call
assert name_lookup is not None, "name_lookup tool should be accessible"
assert normalize_nodes is not None, "normalize_nodes tool should be accessible"
assert normalize_nodes is not None, "normalize_nodes tool should be accessible"
Loading