-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ModelingLLM
: Add Structured Grading Instruction Generation and Restructure Module
#340
Merged
maximiliansoelch
merged 24 commits into
develop
from
features/modeling/structured-grading-instructions
Sep 23, 2024
+542
−411
Merged
Changes from 11 commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
6211745
Rewrite Modeling Module to process all UML Diagram types
LeonWehrhahn 8c86453
Update module to process non-graded feedback requests
LeonWehrhahn e426766
Merge develop
LeonWehrhahn 93ab9bf
Merge develop
LeonWehrhahn b938606
Structured Grading
LeonWehrhahn 6b6a060
Add structured grading instruction generation and restructure modelin…
LeonWehrhahn 89208bd
Add structured grading instruction generation and restructure modelin…
LeonWehrhahn 4ff35da
Ignore .gradle directories
LeonWehrhahn 0af7b89
Merge branch 'features/modeling/structured-grading-instructions' of h…
LeonWehrhahn 42db368
Refactor transform_grading_criteria to transform_grading_criterion
LeonWehrhahn 1266b22
Use GradingCriterion instead of custom model
LeonWehrhahn 112a964
Merge remote-tracking branch 'origin/develop' into features/modeling/…
LeonWehrhahn 132b041
Remove duplicate configuration for Module Modeling LLM
LeonWehrhahn 7678f60
Fix lint
LeonWehrhahn 32f35b2
Update import statements for StructuredGradingCriterion
LeonWehrhahn 2c8a0fc
Refactor convert_to_athana_feedback_model.py to set is_graded to Fals…
LeonWehrhahn 1f2a73c
Merge branch 'develop' into features/modeling/structured-grading-inst…
dmytropolityka 7eccf05
Refactor import statements for UMLParser and related classes
LeonWehrhahn ec7147b
Merge branch 'features/modeling/structured-grading-instructions' of h…
LeonWehrhahn 7062c2d
Refactor convert_to_athana_feedback_model.py to remove unnecessary pa…
LeonWehrhahn 8c83fd8
Refactor convert_to_athana_feedback_model.py to remove unnecessary pa…
LeonWehrhahn 6538117
Refactor max_tokens parameter in openai.py to increase the limit for …
LeonWehrhahn c70f1db
Adjust prompts and apollon
LeonWehrhahn 6e3e908
Update postcss version in package-lock.json
LeonWehrhahn File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
35 changes: 35 additions & 0 deletions
35
...g/module_modeling_llm/module_modeling_llm/apollon_transformer/apollon_json_transformer.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import json | ||
|
||
from module_modeling_llm.apollon_transformer.parser.uml_parser import UMLParser | ||
|
||
|
||
class ApollonJSONTransformer: | ||
|
||
@staticmethod | ||
def transform_json(model: str) -> tuple[str, dict[str, str], str]: | ||
""" | ||
Serialize a given Apollon diagram model to a string representation. | ||
This method converts the UML diagram model into a format similar to mermaid syntax, called "apollon". | ||
|
||
:param model: The Apollon diagram model to serialize. | ||
:return: A tuple containing the serialized model as a string and a dictionary mapping element and relation names | ||
to their corresponding IDs. | ||
""" | ||
|
||
model_dict = json.loads(model) | ||
|
||
parser = UMLParser(model_dict) | ||
|
||
diagram_type = model_dict.get("type", "unknown") | ||
|
||
# Convert the UML diagram to the apollon representation | ||
apollon_representation = parser.to_apollon() | ||
|
||
# Extract elements and relations with their corresponding IDs and names | ||
names = { | ||
**{element['name']: element['id'] for element in parser.get_elements()}, | ||
**{relation['name']: relation['id'] for relation in parser.get_relations()} | ||
} | ||
|
||
return apollon_representation, names, diagram_type | ||
|
41 changes: 41 additions & 0 deletions
41
...es/modeling/module_modeling_llm/module_modeling_llm/apollon_transformer/parser/element.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
from typing import Dict, Any, List, Optional | ||
|
||
class Element: | ||
def __init__(self, data: Dict[str, Any], element_dict: Optional[Dict[str, Any]] = None): | ||
self.id: str = data.get('id', '') | ||
self.type: str = data.get('type', '') | ||
self.name: str = data.get('name', '') | ||
self.owner: str = data.get('owner', '') | ||
self.attribute_refs: List[str] = data.get('attributes', []) | ||
self.method_refs: List[str] = data.get('methods', []) | ||
self.attributes: List[str] = [] | ||
self.methods: List[str] = [] | ||
if element_dict is not None: | ||
self.resolve_references(element_dict) | ||
|
||
def resolve_references(self, element_dict: Dict[str, Any]): | ||
self.attributes = [element_dict[ref].get("name", "") for ref in self.attribute_refs if ref in element_dict] | ||
self.methods = [element_dict[ref].get('name', '') for ref in self.method_refs if ref in element_dict] | ||
|
||
for ref_list, target_list in [(self.attribute_refs, self.attributes), (self.method_refs, self.methods)]: | ||
target_list.extend( | ||
element_dict.get(ref, {}).get("name", "") for ref in ref_list if ref in element_dict | ||
) | ||
|
||
def to_apollon(self) -> str: | ||
parts = [f"[{self.type}] {self.name}"] | ||
|
||
if self.attributes or self.methods: | ||
details = [] | ||
if self.attributes: | ||
details.append(f" attributes:") | ||
details.extend(f" {attr}" for attr in self.attributes) | ||
if self.methods: | ||
details.append(f" methods:") | ||
details.extend(f" {method}" for method in self.methods) | ||
parts.append("{\n" + "\n".join(details) + "\n}") | ||
|
||
return " ".join(parts) | ||
|
||
def __getitem__(self, key): | ||
return self.__dict__[key] |
96 changes: 96 additions & 0 deletions
96
...s/modeling/module_modeling_llm/module_modeling_llm/apollon_transformer/parser/relation.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
from typing import Dict, Any, List, Optional | ||
|
||
class Relation: | ||
def __init__(self, data: Dict[str, Any], element_dict: Optional[Dict[str, Any]], index: int): | ||
self.id: str = data.get('id', '') | ||
self.type: str = data.get('type', '') | ||
# Check if flowType exists, if so use that as the type | ||
self.type = data.get('flowType', self.type) | ||
self.label: str = data.get('name', '') | ||
self.source: Dict[str, Any] = data.get('source', {}) | ||
self.target: Dict[str, Any] = data.get('target', {}) | ||
self.messages: List[Dict[str, str]] = data.get('message', []) | ||
self.name = f"R{index}" | ||
if element_dict is not None: | ||
self.resolve_references(element_dict) | ||
|
||
def resolve_references(self, element_dict: Dict[str, Any]): | ||
if self.source['element'] in element_dict: | ||
self.source['element'] = element_dict[self.source['element']].get("name", "") | ||
if self.target['element'] in element_dict: | ||
self.target['element'] = element_dict[self.target['element']].get("name", "") | ||
|
||
def to_apollon(self) -> str: | ||
parts = [f"{self.name}: {self.source['element']} {get_relation_arrow(self.type)} {self.target['element']}"] | ||
|
||
if self.label: | ||
parts[0] += f": {self.label}" | ||
|
||
details = [] | ||
for end in ['source', 'target']: | ||
end_data = getattr(self, end) | ||
if 'role' in end_data or 'multiplicity' in end_data: | ||
end_details = [f" {end_data['element']}: {{"] | ||
if 'role' in end_data: | ||
end_details.append(f" role: {end_data['role']}") | ||
if 'multiplicity' in end_data: | ||
end_details.append(f" multiplicity: {end_data['multiplicity']}") | ||
end_details.append(" }") | ||
details.extend(end_details) | ||
|
||
if self.messages: | ||
details.append(" messages: [") | ||
for message in self.messages: | ||
to_element = self.target['element'] if message['direction'] == 'target' else self.source['element'] | ||
details.append(f" {{ name: {message['name']}, to_direction: {to_element} }}") | ||
details.append(" ]") | ||
|
||
if details: | ||
parts.append("{\n" + "\n".join(details) + "\n}") | ||
|
||
return " ".join(parts) | ||
|
||
def __getitem__(self, key): | ||
return self.__dict__[key] | ||
|
||
def get_relation_arrow(relation_type: str) -> str: | ||
""" | ||
Returns the correct arrow based on the relation type or flow type using string containment. | ||
|
||
Parameters: | ||
relation_type (str): The type of the relation (e.g., "ClassAggregation", "BPMNFlow_sequence"). | ||
|
||
Returns: | ||
str: The arrow representation for the given relation type in Mermaid syntax. | ||
""" | ||
arrow_map = { | ||
# Keys sorted manually by length in descending order to ensure correct matching when using endswith, e.g., when we have dataassociation, dataassociation should be checked before association | ||
"interfacerequired": "--c", | ||
"interfaceprovided": "--", | ||
"dataassociation": "-->", | ||
"generalization": "<|--", | ||
"unidirectional": "-->", | ||
"bidirectional": "<-->", | ||
"association": "-->", | ||
"inheritance": "<|--", | ||
"composition": "*--", | ||
"aggregation": "o--", | ||
"realization": "..|>", | ||
"dependency": "..>", | ||
"sequence": "-->", | ||
"message": "-->", | ||
"include": "..>", | ||
"message": "-->", | ||
"extend": "-->", | ||
"flow": "-->", | ||
"link": "-->", | ||
"arc": "-->", | ||
} | ||
|
||
relation_type = relation_type.replace(" ", "").lower() | ||
|
||
for key in arrow_map: | ||
if relation_type.endswith(key): | ||
return f"({relation_type}) {arrow_map[key]}" | ||
|
||
return f"-- {relation_type} --" |
84 changes: 84 additions & 0 deletions
84
...modeling/module_modeling_llm/module_modeling_llm/apollon_transformer/parser/uml_parser.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
from typing import Dict, Any, List | ||
from string import ascii_uppercase | ||
|
||
from module_modeling_llm.apollon_transformer.parser.element import Element | ||
from module_modeling_llm.apollon_transformer.parser.relation import Relation | ||
|
||
|
||
class UMLParser: | ||
def __init__(self, json_data: Dict[str, Any]): | ||
self.data = json_data | ||
self.title = self.data['type'] | ||
self.elements: List[Element] = [] | ||
self.relations: List[Relation] = [] | ||
self.owners: Dict[str, List[str]] = {} | ||
self._parse() | ||
|
||
def _parse(self): | ||
name_counts = {} | ||
referenced_ids : List[str] = [] | ||
name_suffix_counters = {} | ||
|
||
# Get all referenced attributes and methods | ||
for element_data in self.data['elements'].values(): | ||
referenced_ids.extend(element_data.get('attributes', [])) | ||
referenced_ids.extend(element_data.get('methods', [])) | ||
|
||
# Count occurrences of each name | ||
for element_data in self.data['elements'].values(): | ||
name = element_data.get('name') | ||
name_counts[name] = name_counts.get(name, 0) + 1 | ||
name_suffix_counters[name] = 0 | ||
|
||
# Filter elements and ensure unique names for duplicates | ||
# This filters out all Elements that are referenced by any other Element, as they are attributes or methods | ||
for element_data in self.data['elements'].values(): | ||
if element_data.get('id') not in referenced_ids: | ||
name = element_data.get('name') | ||
if name_counts[name] > 1: | ||
suffix_index = name_suffix_counters[name] | ||
element_data['name'] = f"{name}{ascii_uppercase[suffix_index]}" | ||
name_suffix_counters[name] += 1 | ||
|
||
element = Element(element_data, self.data['elements']) | ||
self.elements.append(element) | ||
|
||
# Parse relations | ||
for index, relation_data in enumerate(self.data['relationships'].values()): | ||
relation = Relation(relation_data, self.data['elements'], index + 1) | ||
self.relations.append(relation) | ||
|
||
# Get all owners and their elements | ||
for element in self.elements: | ||
ownerId = element.owner | ||
if ownerId: | ||
owner_element = next((el for el in self.elements if el.id == ownerId), None) | ||
if owner_element: | ||
ownerName = owner_element.name | ||
if ownerName not in self.owners: | ||
self.owners[ownerName] = [] | ||
self.owners[ownerName].append(element.name) | ||
|
||
def to_apollon(self) -> str: | ||
lines = [f"UML Diagram Type: {self.title}", ""] | ||
|
||
if self.elements: | ||
lines.append("@Elements:\n") | ||
lines.extend(element.to_apollon() for element in self.elements) | ||
|
||
if self.relations: | ||
lines.append("\n\n@Relations:\n") | ||
lines.extend(relation.to_apollon() for relation in self.relations) | ||
|
||
if self.owners: | ||
lines.append("\n\n@Owners:\n") | ||
for owner, children in self.owners.items(): | ||
lines.append(f"{owner}: {', '.join(children)}") | ||
|
||
return "\n".join(lines) | ||
|
||
def get_elements(self) -> List[Element]: | ||
return self.elements | ||
|
||
def get_relations(self) -> List[Relation]: | ||
return self.relations |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to give the class a more specific name? Element seems to be too general.