Skip to content

Commit

Permalink
Merge pull request #15 from EURAC-EEBgroup/pre/beta
Browse files Browse the repository at this point in the history
TTL to Description, Custom logger and Improvements
  • Loading branch information
PeriniM authored Nov 11, 2024
2 parents 2372873 + a7d9112 commit 0f295d8
Show file tree
Hide file tree
Showing 24 changed files with 341 additions and 34 deletions.
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
## [1.2.0-beta.3](https://github.com/EURAC-EEBgroup/brick-llm/compare/v1.2.0-beta.2...v1.2.0-beta.3) (2024-11-11)


### Perf

* add custom logging level ([82bead2](https://github.com/EURAC-EEBgroup/brick-llm/commit/82bead23929a23998f4d7504f31d83bf253927bf))

## [1.2.0-beta.2](https://github.com/EURAC-EEBgroup/brick-llm/compare/v1.2.0-beta.1...v1.2.0-beta.2) (2024-11-11)


### Bug Fixes

* improved ttl description generation ([bf4bdae](https://github.com/EURAC-EEBgroup/brick-llm/commit/bf4bdaecca779ff835e2349bfcdff7a3d17ead0e))

## [1.2.0-beta.1](https://github.com/EURAC-EEBgroup/brick-llm/compare/v1.1.2...v1.2.0-beta.1) (2024-11-11)


### Features

* ttl_to_building_description function ([d83e66f](https://github.com/EURAC-EEBgroup/brick-llm/commit/d83e66f5851a4db71825c925389d3183cb10faaf))


### Docs

* imporeved docs and ttl_output method ([24a7d8b](https://github.com/EURAC-EEBgroup/brick-llm/commit/24a7d8ba1d94fa25c0d6c5db33b38853bc2d5ff2))

## [1.1.2](https://github.com/EURAC-EEBgroup/brick-llm/compare/v1.1.1...v1.1.2) (2024-11-05)


Expand Down
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

BrickLLM is a Python library for generating RDF files following the BrickSchema ontology using Large Language Models (LLMs).

## Features
## 🧰 Features

- Generate BrickSchema-compliant RDF files from natural language descriptions of buildings and facilities
- Support for multiple LLM providers (OpenAI, Anthropic, Fireworks)
Expand Down Expand Up @@ -224,12 +224,12 @@ brick_graph_local.save_ttl_output("my_building_local.ttl")
## 📖 Documentation
For more detailed information on how to use BrickLLM, please refer to our [documentation](https://brickllm.com/docs).
For more detailed information on how to use BrickLLM, please refer to our [documentation](https://eurac-eebgroup.github.io/brick-llm/).
## ▶️ Web Application
A web app is available to use the library directly through an interface at the following link ().
The application can also be used locally as described in the dedicated repository [BrickLLM App](https://github.com/EURAC-EEBgroup/Brick_ontology_tool).
A web app is available to use the library directly through an interface at the following link ().
The application can also be used locally as described in the dedicated repository [BrickLLM App](https://github.com/EURAC-EEBgroup/Brick_ontology_tool).
**Note**: The tool is currently being deployed on our servers and on the MODERATE platform. It will be online shortly !
Expand All @@ -241,25 +241,25 @@ We welcome contributions to BrickLLM! Please see our [contributing guidelines](C
BrickLLM is released under the BSD-3-Clause License. See the [LICENSE](LICENSE) file for details.
## Contact
## 📧 Contact
For any questions or support, please contact:
- Marco Perini <[email protected]>
- Daniele Antonucci <[email protected]>
- Rocco Giudice <[email protected]>
## Citation
## 📝 Citation
Please cite us if you use the library
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.14039358.svg)](https://zenodo.org/doi/10.5281/zenodo.14039358)
## 💙 Aknwoledegments
This work was carried out within European projects:
## 💙 Acknowledgements
This work was carried out within European projects:
<p align="center">
<img src="https://raw.githubusercontent.com/EURAC-EEBgroup/brick-llm/refs/heads/main/docs/assets/moderate_logo.png" alt="Moderate"
<img src="https://raw.githubusercontent.com/EURAC-EEBgroup/brick-llm/refs/heads/main/docs/assets/moderate_logo.png" alt="Moderate"
</p>
Moderate - Horizon Europe research and innovation programme under grant agreement No 101069834, with the aim of contributing to the development of open products useful for defining plausible scenarios for the decarbonization of the built environment
Expand Down
10 changes: 9 additions & 1 deletion brickllm/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
from .configs import GraphConfig
from .schemas import ElemListSchema, RelationshipsSchema, TTLSchema
from .logger import custom_logger
from .schemas import (
ElemListSchema,
RelationshipsSchema,
TTLSchema,
TTLToBuildingPromptSchema,
)
from .states import State, StateLocal

__all__ = [
"ElemListSchema",
"RelationshipsSchema",
"TTLSchema",
"TTLToBuildingPromptSchema",
"State",
"StateLocal",
"GraphConfig",
"custom_logger",
]
26 changes: 17 additions & 9 deletions brickllm/graphs/abstract_graph.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import os
from abc import ABC, abstractmethod
from typing import Any, Dict, List, Union
from typing import Any, Dict, List, Tuple, Union

from langchain.chat_models.base import BaseChatModel
from langgraph.graph import StateGraph
from PIL import Image

from ..helpers.llm_models import _get_model
from ..utils import ttl_to_building_prompt


class AbstractBrickSchemaGraph(ABC):
Expand All @@ -16,6 +17,9 @@ def __init__(self, model: Union[str, BaseChatModel] = "openai"):
self.graph = None
self.config = {"configurable": {"thread_id": "1", "llm_model": self.model}}
self.result = None
self.ttl_output = None
self.generated_building_description = None
self.generated_key_elements = None

@abstractmethod
def build_graph(self):
Expand Down Expand Up @@ -75,12 +79,16 @@ def save_ttl_output(self, output_file: str = "brick_schema_output.ttl") -> None:
if self.result is None:
raise ValueError("No result found. Please run the graph first.")

ttl_output = self.result.get("ttl_output", None)
if self.ttl_output is None:
raise ValueError("No TTL output found. Please run the graph first.")

if ttl_output:
with open(output_file, "w") as f:
f.write(ttl_output)
else:
raise ValueError(
"No TTL output found in the result. Please run the graph with a valid prompt."
)
with open(output_file, "w") as f:
f.write(self.ttl_output)

def ttl_to_building_description(self) -> Tuple[str, List[str]]:
if self.ttl_output is None:
raise ValueError("No TTL output found. Please run the graph first.")
self.generated_building_description, self.generated_key_elements = (
ttl_to_building_prompt(self.ttl_output, self.model)
)
return self.generated_building_description, self.generated_key_elements
2 changes: 2 additions & 0 deletions brickllm/graphs/brickschema_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ def run(
):
events.append(event)
self.result = events[-1]
self.ttl_output = self.result.get("ttl_output", None)
return events
else:
self.result = self.graph.invoke(input_data, self.config)
self.ttl_output = self.result.get("ttl_output", None)
return self.result
2 changes: 2 additions & 0 deletions brickllm/graphs/brickschema_graph_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ def run(
):
events.append(event)
self.result = events[-1]
self.ttl_output = self.result.get("ttl_output", None)
return events
else:
self.result = self.graph.invoke(input_data, self.config)
self.ttl_output = self.result.get("ttl_output", None)
return self.result
2 changes: 2 additions & 0 deletions brickllm/helpers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
prompt_template_local,
schema_to_ttl_instructions,
ttl_example,
ttl_to_user_prompt,
)

__all__ = [
Expand All @@ -16,4 +17,5 @@
"schema_to_ttl_instructions",
"ttl_example",
"prompt_template_local",
"ttl_to_user_prompt",
]
33 changes: 27 additions & 6 deletions brickllm/helpers/prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
You are now asked to identify the elements presents in the user prompt, even if not explicitly mentioned.\n
USER PROMPT: {prompt} \n
ELEMENTS: {elements_dict} \n
"""
""" # noqa

get_elem_children_instructions: str = """
You are a BrickSchema ontology expert and you are provided with a user prompt which describes a building or facility.\n
Expand All @@ -22,7 +22,7 @@
USER PROMPT: {prompt} \n
ELEMENTS HIERARCHY: {elements_list} \n
"""
""" # noqa

get_relationships_instructions: str = """
You are a BrickSchema ontology expert and are provided with a detailed description of a building or facility.\n
Expand All @@ -35,7 +35,7 @@
If an element has no relationships, add an empty string in place of the missing component ("Room.1","").\n
Hierarchical structure: {building_structure}\n
USER PROMPT: {prompt}
"""
""" # noqa

ttl_example: str = """
@prefix bldg: <urn:Building#> .
Expand Down Expand Up @@ -84,7 +84,7 @@
bldg:livingroom a brick:Room ;
brick:isPartOf bldg:Milano_Residence_1 .
"""
""" # noqa

schema_to_ttl_instructions: str = """
You are a BrickSchema ontology expert and you are provided with a user prompt which describes a building or facility.\n
Expand All @@ -102,7 +102,28 @@
USER DESCRIPTION: {prompt}\n
COMPONENTS DICT: {sensors_dict}\n
"""
""" # noqa

ttl_to_user_prompt: str = """
You are a BrickSchema ontology expert tasked with generating a clear and concise description of a building or facility from a TTL script.
Your output must follow these guidelines:
- Focus on the key building characteristics, components and relationships present in the TTL
- Maintain technical accuracy and use proper Brick terminology
- Keep descriptions clear and well-structured
- Only include information explicitly stated in the TTL script
- If no TTL content is provided, return an empty string
Eventually, the user can provide additional instructions to help you generate the building description.
<additional_instructions>
{additional_instructions}
</additional_instructions>
TTL script to analyze:
<ttl_script>
{ttl_script}
</ttl_script>
""" # noqa

prompt_template_local: str = """
Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.
Expand All @@ -113,4 +134,4 @@
{user_prompt}
### Response:
"""
""" # noqa
38 changes: 38 additions & 0 deletions brickllm/logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import logging

# Create custom log level
EURAC_LEVEL = 25
logging.addLevelName(EURAC_LEVEL, "EURAC")


def eurac(self, message, *args, **kwargs):
"""
Log with custom EURAC level
"""
if self.isEnabledFor(EURAC_LEVEL):
self._log(EURAC_LEVEL, message, args, **kwargs)


# Add eurac method to Logger class
logging.Logger.eurac = eurac


# Create and configure logger
def get_logger(name="BrickLLM"):
logger = logging.getLogger(name)

# Create handler if none exists
if not logger.handlers:
handler = logging.StreamHandler()
formatter = logging.Formatter(
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
handler.setFormatter(formatter)
logger.addHandler(handler)

logger.setLevel(EURAC_LEVEL)
return logger


# Create default logger instance
custom_logger = get_logger()
3 changes: 2 additions & 1 deletion brickllm/nodes/generation_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from .. import StateLocal
from ..helpers import prompt_template_local
from ..logger import custom_logger
from ..utils import extract_rdf_graph


Expand All @@ -17,7 +18,7 @@ def generation_local(state: StateLocal, config: Dict[str, Any]) -> Dict[str, Any
dict: A dictionary containing the output generated.
"""

print("---One shot generation with local LLM Node---")
custom_logger.eurac("🤖 Starting one shot generation with local LLM")

instructions = state["instructions"]
user_prompt = state["user_prompt"]
Expand Down
5 changes: 4 additions & 1 deletion brickllm/nodes/get_elem_children.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from .. import ElemListSchema, State
from ..helpers import get_elem_children_instructions
from ..logger import custom_logger
from ..utils import create_hierarchical_dict, filter_elements, get_children_hierarchy


Expand All @@ -18,7 +19,9 @@ def get_elem_children(state: State, config: Dict[str, Any]) -> Dict[str, Any]:
Returns:
dict: A dictionary containing the hierarchical structure of identified elements.
"""
print("---Get Elem Children Node---")
custom_logger.eurac(
"📊 Getting children for each BrickSchema category in the element list"
)

user_prompt = state["user_prompt"]
categories = state["elem_list"]
Expand Down
3 changes: 2 additions & 1 deletion brickllm/nodes/get_elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from .. import ElemListSchema, State
from ..helpers import get_elem_instructions
from ..logger import custom_logger
from ..utils import get_brick_definition, get_hierarchical_info


Expand All @@ -19,7 +20,7 @@ def get_elements(state: State, config: Dict[str, Any]) -> Dict[str, Any]:
Returns:
dict: A dictionary containing the list of identified elements.
"""
print("---Get Elements Node---")
custom_logger.eurac("🔍 Getting elements from user prompt")

user_prompt = state["user_prompt"]

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

from .. import RelationshipsSchema, State
from ..helpers import get_relationships_instructions
from ..logger import custom_logger
from ..utils import build_hierarchy, find_sensor_paths


Expand All @@ -20,7 +21,7 @@ def get_relationships(state: State, config: Dict[str, Any]) -> Dict[str, Any]:
Returns:
dict: A dictionary containing the grouped sensor paths.
"""
print("---Get Relationships Node---")
custom_logger.eurac("🔗 Getting relationships between building components")

user_prompt = state["user_prompt"]
building_structure = state["elem_hierarchy"]
Expand Down
3 changes: 2 additions & 1 deletion brickllm/nodes/get_sensors.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Any, Dict

from .. import State
from ..logger import custom_logger


def get_sensors(state: State) -> Dict[str, Any]:
Expand All @@ -13,7 +14,7 @@ def get_sensors(state: State) -> Dict[str, Any]:
Returns:
dict: A dictionary containing sensor UUIDs mapped to their locations.
"""
print("---Get Sensor Node---")
custom_logger.eurac("📡 Getting sensors information")

uuid_dict = {
"Building#1>Floor#1>Office#1>Room#1": [
Expand Down
Loading

0 comments on commit 0f295d8

Please sign in to comment.