-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add synthesis and diagrams in PNG format
- Loading branch information
1 parent
4597f0f
commit 185c82a
Showing
10 changed files
with
226 additions
and
1 deletion.
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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,192 @@ | ||
from base64 import b64encode | ||
from collections.abc import Iterable, Iterator | ||
from re import compile, findall, MULTILINE, sub | ||
|
||
from openai import OpenAI | ||
|
||
from stpa.definitions import Definition, UnsafeControlAction | ||
from stpa.utilities import YesOrNoResponse | ||
|
||
|
||
EQUALITY_QUERY_MODEL = 'gpt-4o' | ||
EQUALITY_QUERY_PROMPT = ''' | ||
Are the STPA Definitions {} and {} saying similar things? | ||
{} | ||
{} | ||
Answer only "YES" or "NO". | ||
'''.strip() | ||
|
||
|
||
def query_equality( | ||
client: OpenAI, | ||
definition_0: Definition, | ||
definition_1: Definition, | ||
seed: int | None = None, | ||
) -> bool: | ||
prompt = EQUALITY_QUERY_PROMPT.format( | ||
definition_0.name, | ||
definition_1.name, | ||
definition_0, | ||
definition_1, | ||
) | ||
completion = client.chat.completions.create( | ||
model=EQUALITY_QUERY_MODEL, | ||
messages=[{'role': 'user', 'content': prompt}], | ||
seed=seed, | ||
) | ||
response = completion.choices[0].message.content | ||
|
||
match response: | ||
case YesOrNoResponse.YES: | ||
status = True | ||
case YesOrNoResponse.NO: | ||
status = False | ||
case _: | ||
raise ValueError(r'cannot determine verdict of {repr(response)}') | ||
|
||
return status | ||
|
||
|
||
_DEFINITION_LINKS_PATTERN = compile(r'\s+\[.*?\]$') | ||
|
||
|
||
def _get_definition_name_body(definition: Definition) -> str: | ||
return sub(_DEFINITION_LINKS_PATTERN, '', str(definition)) | ||
|
||
|
||
_DEFINITION_NAME_PATTERN = compile(r'^.+?:\s+') | ||
|
||
|
||
def _get_definition_body(definition: Definition) -> str: | ||
body = _get_definition_name_body(definition) | ||
|
||
return sub(_DEFINITION_NAME_PATTERN, '', body) | ||
|
||
|
||
def _number_lines(lines: Iterable[str]) -> Iterator[str]: | ||
for i, line in enumerate(lines): | ||
yield f'{i + 1}. {line}' | ||
|
||
|
||
_NUMBER_PATTERN = compile(r'^\d+\. ') | ||
|
||
|
||
def _unnumber_lines(lines: Iterable[str]) -> Iterator[str]: | ||
for line in lines: | ||
yield sub(_NUMBER_PATTERN, '', line) | ||
|
||
|
||
RAW_UNSAFE_CONTROL_ACTIONS_GENERATION_MODEL = 'gpt-4o' | ||
RAW_UNSAFE_CONTROL_ACTIONS_GENERATION_PROMPT = ''' | ||
Generate {} unsafe control actions of the system shown in the control\ | ||
structure diagram. | ||
An unsafe control action follows one of the four patterns: 1) not providing\ | ||
causes hazard 2) providing causes hazard 3) too early, too late, out of order\ | ||
or 4) stopped too soon, applied too long. | ||
Examples: | ||
{} | ||
Write each unsafe control action in a single line, preceded by a number. | ||
'''.strip() | ||
RAW_UNSAFE_CONTROL_ACTION_PATTERN = compile(r'^\d+\. .+$', MULTILINE) | ||
|
||
|
||
def generate_raw_unsafe_control_actions( | ||
client: OpenAI, | ||
count: int, | ||
example_unsafe_control_actions: Iterable[UnsafeControlAction], | ||
diagram_pathname: str, | ||
seed: int | None = None, | ||
) -> list[str]: | ||
example_lines = _number_lines( | ||
map(_get_definition_body, example_unsafe_control_actions), | ||
) | ||
prompt = RAW_UNSAFE_CONTROL_ACTIONS_GENERATION_PROMPT.format( | ||
count, | ||
'\n'.join(example_lines), | ||
) | ||
|
||
with open(diagram_pathname, 'rb') as file: | ||
base64_image = b64encode(file.read()).decode('utf-8') | ||
|
||
completion = client.chat.completions.create( | ||
model=RAW_UNSAFE_CONTROL_ACTIONS_GENERATION_MODEL, | ||
messages=[ | ||
{ | ||
'role': 'user', | ||
'content': [ | ||
{ | ||
'type': 'text', | ||
'text': prompt, | ||
}, | ||
{ | ||
'type': 'image_url', | ||
'image_url': { | ||
'url': f'data:image/jpeg;base64,{base64_image}', | ||
}, | ||
}, | ||
], | ||
}, | ||
], | ||
seed=seed, | ||
) | ||
response = completion.choices[0].message.content | ||
|
||
assert isinstance(response, str) | ||
|
||
raw_unsafe_control_actions = list( | ||
_unnumber_lines( | ||
map( | ||
str.strip, | ||
findall(RAW_UNSAFE_CONTROL_ACTION_PATTERN, response), | ||
), | ||
), | ||
) | ||
|
||
return raw_unsafe_control_actions | ||
|
||
|
||
LINKAGE_QUERY_MODEL = 'gpt-4o' | ||
LINKAGE_QUERY_PROMPT = ''' | ||
Should the STPA Definition {} be linked to Definition {}? | ||
{} | ||
{} | ||
Answer only "YES" or "NO". | ||
'''.strip() | ||
|
||
|
||
def query_linkage( | ||
client: OpenAI, | ||
linked_definition: Definition, | ||
linking_definition: Definition, | ||
seed: int | None = None, | ||
) -> bool: | ||
prompt = LINKAGE_QUERY_PROMPT.format( | ||
linked_definition.name, | ||
linking_definition.name, | ||
_get_definition_name_body(linked_definition), | ||
_get_definition_name_body(linking_definition), | ||
) | ||
completion = client.chat.completions.create( | ||
model=LINKAGE_QUERY_MODEL, | ||
messages=[{'role': 'user', 'content': prompt}], | ||
seed=seed, | ||
) | ||
response = completion.choices[0].message.content | ||
|
||
match response: | ||
case YesOrNoResponse.YES: | ||
status = True | ||
case YesOrNoResponse.NO: | ||
status = False | ||
case _: | ||
raise ValueError(r'cannot determine verdict of {repr(response)}') | ||
|
||
return status |
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