Skip to content

Commit c293201

Browse files
committed
added curies as a tool
1 parent be9a151 commit c293201

File tree

7 files changed

+89
-28
lines changed

7 files changed

+89
-28
lines changed

poetry.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ langchain-ollama = "^0.1.1"
1818
langchain-anthropic = "^0.1.22"
1919
langchain-chroma = "^0.1.3"
2020
pystow = "^0.5.4"
21+
curies = "^0.7.10"
2122

2223
[tool.poetry.group.dev.dependencies]
2324
pytest = {version = ">=8.3.2"}

src/llm_change_agent/cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from llm_change_agent.constants import PROVIDER_DEFAULT_MODEL_MAP
1010
from llm_change_agent.evaluations.evaluator import run_evaluate
1111
from llm_change_agent.llm_agent import LLMChangeAgent
12-
from llm_change_agent.utils.click_utils import validate_path_or_url_or_ontology
12+
from llm_change_agent.utils.general_utils import validate_path_or_url_or_ontology
1313
from llm_change_agent.utils.llm_utils import (
1414
get_anthropic_models,
1515
get_lbl_cborg_models,

src/llm_change_agent/evaluations/evaluator.py

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,13 @@
2222
PR_CLOSED_ISSUES_KEY,
2323
PULL_REQUESTS_KEY,
2424
)
25-
from llm_change_agent.utils.llm_utils import extract_commands
25+
from llm_change_agent.utils.llm_utils import extract_commands, normalize_changes
2626

2727
logger = logging.getLogger(__name__)
2828
logger.info("Evaluating the LLM Change Agent.")
2929

3030

31+
3132
def download_document(url, input_dir):
3233
"""Download the document from the URL."""
3334
if not os.path.exists(input_dir):
@@ -115,6 +116,7 @@ def run_llm_change_agent(prompt, provider, model, docs: List[Any] = None) -> Lis
115116
ctx.params["model"] = model
116117
ctx.params["docs"] = docs
117118
response = extract_commands(execute.invoke(ctx))
119+
print(response)
118120
kgcl_commands = [command for command in ast.literal_eval(response)]
119121
return kgcl_commands
120122

@@ -172,11 +174,44 @@ def generate_changes_via_llm(eval_dir, output_dir, provider, model):
172174
print(f"Predicted changes saved to {output_sub_dir}")
173175

174176

175-
def compare_changes():
177+
def compare_changes(expected_dir:Path, output_dir:Path):
176178
"""Compare the actual changes with the predicted changes."""
177-
import pdb
179+
# For each document in the expected directory, there is a corresponding document in the output directory
180+
181+
output_files = list(output_dir.rglob("*.yaml"))
182+
183+
# output_files_dict is : {provider_model: {filename: file_path}}
184+
output_files_list_of_dicts = [
185+
{f"{file.parts[-3]}_{file.parts[-2]}": {file.name:file}} for file in output_files
186+
]
187+
188+
for model_output in output_files_list_of_dicts:
189+
for provider_model, file_info in model_output.items():
190+
for filename, filepath in file_info.items():
191+
filename = filepath.name
192+
expected_file = expected_dir / filename
193+
output_file = filepath
194+
with open(expected_file, "r") as ex , open(output_file, "r") as out:
195+
expected_yaml = yaml.safe_load(ex)
196+
output_yaml = yaml.safe_load(out)
197+
expected_yaml_subset = {k: v for k, v in expected_yaml.items() if k in output_yaml}
198+
for pr_id, output_changes in output_yaml.items():
199+
expected_change = expected_yaml_subset.get(pr_id)
200+
if len(output_changes) > 0:
201+
compare_output_vs_expected(expected_change, output_changes)
202+
203+
204+
205+
def compare_output_vs_expected(expected_changes, output_changes:List):
206+
"""Compare the expected changes with the output changes."""
207+
output_changes = normalize_changes(output_changes)
208+
accuracy = 0.0
209+
total = len(expected_changes)
210+
correct = 0
211+
import pdb; pdb.set_trace()
212+
213+
178214

179-
pdb.set_trace()
180215

181216

182217
def run_evaluate(model: str, provider: str):
@@ -194,22 +229,5 @@ def run_evaluate(model: str, provider: str):
194229

195230
generate_changes_via_llm(model=model, provider=provider, eval_dir=eval_dir, output_dir=output_dir)
196231

197-
# compare_changes()
198-
199-
# logger.info("Split the YAML documents randomly into RAG and Evaluation documents 80% and 20%.")
200-
# random.shuffle(ONTODIFF_DOCS)
201-
# split_index = int(len(ONTODIFF_DOCS) * 0.8)
202-
# rag_docs = ONTODIFF_DOCS[:split_index]
203-
# eval_docs = ONTODIFF_DOCS[split_index:]
204-
205-
# logger.info("Run llm_change_agent with the RAG documents.")
206-
# run_llm_change_agent(rag_docs)
207-
208-
# logger.info("Run the evaluation script with the Evaluation documents.")
209-
# run_evaluation_script(eval_docs)
210-
211-
# logger.info("Compare the actual `changes` with the predicted `changes` from the llm_change_agent.")
212-
# compare_changes()
232+
compare_changes(expected_dir=expected_dir, output_dir=output_dir)
213233

214-
# logger.info("Evaluation completed.")
215-
# return

src/llm_change_agent/templates/templates.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,17 @@ def get_issue_analyzer_template():
1313
You have the following tools at your disposal to help you with this task:
1414
{tools}
1515
You also have the KGCL grammar in lark format: {grammar} along with an explanation of the grammar: {explanation}.
16-
You MUST use CURIEs/IRIs for every entity and relationship. You've been provided with JSON documents to find CURIEs/IRIs
16+
You MUST use CURIEs for every entity and relationship. You've been provided with JSON documents to find CURIEs/IRIs
1717
for entities and relationships. Do not manufacture CURIEs/IRIs. Make sure it is retrieved from these
18-
documents if absent in the GitHub issues provided. The final answer should be JUST a list of KGCL commands, nothing else.
19-
Keep the verbosity of the response to non-existent. It should be concise and to the point.
18+
documents if absent in the GitHub issues provided. If you end up with a IRI to represent an entity, use
19+
the tool 'compress_iri' from {tools} to derive a CURIE from it. If you end up with the label for the entity,
20+
try to retrieve its CURIE/IRI from the JSON docs and get CURIE using {tools}.
21+
22+
For e.g.: if you have a change `delete edge MONDO:0005772 rdfs:subClassOf <immune system disease>`
23+
It should be converted to `delete edge MONDO:0005772 rdfs:subClassOf MONDO:0005046`.
24+
25+
The final answer should be JUST a list of KGCL commands, nothing else.
26+
Keep the verbosity of the response to zero. It should be concise and to the point.
2027
2128
It is fine if you are not able to form any commands. You can just return an empty list.
2229

src/llm_change_agent/utils/click_utils.py renamed to src/llm_change_agent/utils/general_utils.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,17 @@ def validate_path_or_url_or_ontology(ctx, param, value):
3333
raise click.BadParameter(f"{val} is not a valid URL, file path, or ontology name") from None
3434

3535
return validated_values
36+
37+
38+
def jaccard_similarity(statement1, statement2):
39+
"""Calculate the Jaccard similarity coefficient between two statements."""
40+
# Split the statements into sets of words
41+
set1 = set(statement1.split())
42+
set2 = set(statement2.split())
43+
44+
# Calculate the intersection and union of the sets
45+
intersection = set1.intersection(set2)
46+
union = set1.union(set2)
47+
48+
# Calculate the Jaccard similarity coefficient
49+
return len(intersection) / len(union)

src/llm_change_agent/utils/llm_utils.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
from pathlib import Path
66
from typing import Union
77

8+
import curies
89
import yaml
910
from langchain.agents import AgentExecutor
1011
from langchain.agents.react.agent import create_react_agent
1112
from langchain.tools.retriever import create_retriever_tool
13+
from langchain_core.tools import tool
1214
from langchain_chroma import Chroma
1315
from langchain_community.document_loaders import WebBaseLoader
1416
from langchain_core.documents import Document
@@ -272,7 +274,7 @@ def execute_agent(llm, prompt, docs):
272274

273275
retriever = vectorstore.as_retriever(search_kwargs={"k": 1})
274276
tool = create_retriever_tool(retriever, "change_agent_retriever", "Change Agent Retriever")
275-
tools = [tool]
277+
tools = [tool, compress_iri]
276278
template = get_issue_analyzer_template()
277279
react_agent = create_react_agent(llm=llm, tools=tools, prompt=template)
278280
agent_executor = AgentExecutor(agent=react_agent, tools=tools, handle_parsing_errors=True, verbose=True)
@@ -316,3 +318,22 @@ def extract_commands(command):
316318
return match.group(0)
317319
else:
318320
return cleaned_command
321+
322+
def normalize_changes(changes):
323+
for idx, change in enumerate(changes):
324+
if any(string.startswith("<http") or string.startswith("http") for string in change.split()):
325+
iri = [string for string in change.split() if string.startswith("<http")or string.startswith("http")]
326+
# Replace the strings in the list with the curie using converter.compress(item)
327+
for _, item in enumerate(iri):
328+
stripped_item = item.strip('<>')
329+
compressed_item = compress_iri(stripped_item) if compress_iri(stripped_item) else item
330+
# Update the original change list with the compressed item
331+
change = change.replace(item, compressed_item)
332+
changes[idx] = change
333+
return changes
334+
335+
@tool
336+
def compress_iri(iri: str) -> str:
337+
"""Compress the IRI."""
338+
converter = curies.get_obo_converter()
339+
return converter.compress(iri)

0 commit comments

Comments
 (0)