├── requirements.txt ├── tests ├── __init__.py └── test_noema.py ├── MANIFEST.in ├── visu.png ├── .DS_Store ├── noema.gif ├── logoNoema.jpg ├── examples ├── .DS_Store ├── basic_semantic_python.py ├── comment_classifier.py ├── basics.py └── vizualisation.py ├── Noema ├── reset.py ├── BaseGenerator.py ├── information.py ├── substring.py ├── code_gen.py ├── __init__.py ├── atomic_types.py ├── text_gen.py ├── composed_types.py ├── selectors.py ├── Generator.py ├── semPy.py ├── programming_langugages.py ├── noesis_wrapper.py ├── cfg.py └── Subject.py ├── setup.py ├── .gitignore ├── flowchart.md ├── LICENSE ├── diagram.puml ├── README.md └── diagram.mmd /requirements.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include logoNoema.png -------------------------------------------------------------------------------- /visu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbanPerli/Noema-Declarative-AI/HEAD/visu.png -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbanPerli/Noema-Declarative-AI/HEAD/.DS_Store -------------------------------------------------------------------------------- /noema.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbanPerli/Noema-Declarative-AI/HEAD/noema.gif -------------------------------------------------------------------------------- /logoNoema.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbanPerli/Noema-Declarative-AI/HEAD/logoNoema.jpg -------------------------------------------------------------------------------- /examples/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbanPerli/Noema-Declarative-AI/HEAD/examples/.DS_Store -------------------------------------------------------------------------------- /Noema/reset.py: -------------------------------------------------------------------------------- 1 | from .Subject import Subject 2 | 3 | class Reset: 4 | 5 | def __init__(self): 6 | Subject().shared().llm.reset() -------------------------------------------------------------------------------- /Noema/BaseGenerator.py: -------------------------------------------------------------------------------- 1 | 2 | class BaseGenerator(): 3 | 4 | def __init__(self): 5 | self.value = None 6 | self.noesis = None 7 | self.noema = None 8 | -------------------------------------------------------------------------------- /Noema/information.py: -------------------------------------------------------------------------------- 1 | from .Generator import Generator 2 | from .Subject import Subject 3 | 4 | class Information(Generator): 5 | 6 | def execute(self): 7 | display_var = "#"+self.id.replace("self.", "").upper()+":" 8 | noesis = display_var + " " + self.value + "\n" 9 | Subject().shared().llm += noesis 10 | self.value = self.value 11 | self.noema = self.value 12 | self.noesis = noesis 13 | Subject().shared().append_to_chain({"value": self.value, "noema": self.noema, "noesis": self.noesis}) 14 | if Subject().shared().verbose: 15 | print(f"{self.id.replace('self.', '')} = \033[94m{self.noema + f'({self.hint})'}\033[0m") 16 | -------------------------------------------------------------------------------- /examples/basic_semantic_python.py: -------------------------------------------------------------------------------- 1 | from Noema import * 2 | 3 | @Noema 4 | def simple_task(task, parameters): 5 | """You are an incredible Python developer. 6 | Always looking for the best way to write code.""" 7 | task_to_code = Information(f"I want to {task}") 8 | formulation = Sentence("Reformulate the task to be easily understood by a Python developer.") 9 | decomposition = ListOf(Sentence,"Decompose the task into smaller sub-tasks.") 10 | result = SemPy(formulation.value)(parameters) 11 | return result.value 12 | 13 | Subject("../Models/EXAONE-3.5-7.8B-Instruct-Q4_K_M.gguf",verbose=True) 14 | nb_letter = simple_task("Count the occurence of letters in a word", "strawberry") 15 | print(nb_letter) -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | # Lire le contenu du README.md 4 | with open("README.md", "r", encoding="utf-8") as fh: 5 | long_description = fh.read() 6 | 7 | setup( 8 | name='Noema', 9 | version='1.1.3', 10 | description='A declarative way to control LLMs.', 11 | long_description=long_description, 12 | long_description_content_type='text/markdown', 13 | author='Alban Perli', 14 | author_email='alban.perli@gmail.com', 15 | url='https://github.com/AlbanPerli/Noema-Declarative-AI', 16 | packages=find_packages(), 17 | install_requires=[ 18 | 'guidance==0.1.15', 19 | 'varname' 20 | ], 21 | classifiers=[ 22 | 'Programming Language :: Python :: 3', 23 | 'License :: OSI Approved :: Apache Software License', 24 | 'Operating System :: OS Independent', 25 | ], 26 | python_requires='>=3.8', 27 | ) 28 | -------------------------------------------------------------------------------- /tests/test_noema.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | import os 4 | import unittest 5 | 6 | # Add the project directory to the sys.path 7 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 8 | 9 | from Noema import * 10 | 11 | class TestNoema(unittest.TestCase): 12 | def setUp(self): 13 | self.subject = Subject("../Models/Mistral-NeMo-Minitron-8B-Instruct.Q4_K_M.gguf") 14 | 15 | # Test Horizon creation 16 | # Test var creation and storage 17 | # Test Sentence creation 18 | # Test Int creation 19 | # Test Float creation 20 | # Test Bool creation 21 | # Test Select creation 22 | # Test IF/ELSE 23 | # Test Sentence creation from var 24 | # Test Int creation from var 25 | # Test Float creation from var 26 | # Test Bool creation from var 27 | # Test Select creation from var 28 | # Test IF/ELSE from var 29 | # Ajouter un SelectOneOrMore 30 | 31 | if __name__ == '__main__': 32 | unittest.main() 33 | -------------------------------------------------------------------------------- /examples/comment_classifier.py: -------------------------------------------------------------------------------- 1 | from Noema import * 2 | 3 | # Create a new Subject 4 | Subject("../Models/EXAONE-3.5-2.4B-Instruct-Q4_K_M.gguf", verbose=True) 5 | 6 | @Noema 7 | def comment_evaluation(comment): 8 | """ 9 | You are a specialist of comment analysis. 10 | You always produce a deep analysis of the comment. 11 | """ 12 | comment_to_analyse = Information(f"{comment}") 13 | specialists = ["Psychologist", "Product manager", "Satisfaction manager"] 14 | analyse_by_specialists = {} 15 | for specialist in specialists: 16 | analysis = Sentence(f"Analysing the comment as a {specialist}") 17 | analyse_by_specialists[specialist] = analysis.value 18 | 19 | synthesis = Paragraph("Providing a synthesis of the analysis.") 20 | qualify_synthesis = Select("Qualify the synthesis", options=["good", "bad", "neutral"]) 21 | print(f"Synthesis: {synthesis.value} is {qualify_synthesis.value}") 22 | return synthesis.value, analyse_by_specialists 23 | 24 | synthesis, abs = comment_evaluation("This llm is very good!") 25 | 26 | print(synthesis) 27 | print(Subject.shared().noema()) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Byte-compiled / optimized / DLL files 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # Unit test / coverage reports 31 | htmlcov/ 32 | .tox/ 33 | .nox/ 34 | .coverage 35 | .coverage.* 36 | .cache 37 | nosetests.xml 38 | coverage.xml 39 | *.cover 40 | .hypothesis/ 41 | .pytest_cache/ 42 | 43 | # Translations 44 | *.mo 45 | *.pot 46 | 47 | # Django stuff: 48 | *.log 49 | local_settings.py 50 | db.sqlite3 51 | 52 | # Flask stuff: 53 | instance/ 54 | .webassets-cache 55 | 56 | # Scrapy stuff: 57 | .scrapy 58 | 59 | # Sphinx documentation 60 | docs/_build/ 61 | 62 | # Jupyter Notebook 63 | .ipynb_checkpoints 64 | 65 | # IPython 66 | profile_default/ 67 | ipython_config.py 68 | 69 | # Virtual environments 70 | .venv 71 | env/ 72 | ENV/ 73 | 74 | # Mac OS 75 | .DS_Store -------------------------------------------------------------------------------- /Noema/substring.py: -------------------------------------------------------------------------------- 1 | from .Generator import Generator, noema_generator 2 | from .Subject import Subject 3 | from guidance import substring 4 | 5 | 6 | class Substring(Generator): 7 | 8 | hint = "Response format: extract a substring" 9 | 10 | def execute(self): 11 | llm = Subject().shared().llm 12 | noesis = "" 13 | if self.hint != None: 14 | noesis = self.value + f"({self.hint})" + "\n" 15 | else: 16 | noesis = self.value + "\n" 17 | display_var = "#"+self.id.replace("self.", "").upper()+":" 18 | llm += noesis 19 | llm += display_var + " " + substring(self.value, name='response') + "\n" 20 | res = llm["response"] 21 | Subject().shared().llm += display_var + " " + res + "\n" 22 | self.noema = self.value 23 | self.value = res 24 | self.noesis = noesis 25 | Subject().shared().append_to_chain({"value": self.value, "noema": self.noema, "noesis": self.noesis}) 26 | if Subject().shared().verbose: 27 | print(f"{self.id.replace('self.', '')} = \033[93m{res}\033[0m (\033[94m{self.noema + f'({self.hint})'}\033[0m)") 28 | -------------------------------------------------------------------------------- /Noema/code_gen.py: -------------------------------------------------------------------------------- 1 | from .Generator import Generator 2 | from .Subject import Subject 3 | from guidance import gen 4 | 5 | class CodeGenerator(Generator): 6 | regex = None 7 | hint = "Response format: code" 8 | return_type = str 9 | 10 | def execute(self, max_tokens=500): 11 | print("Code Gen Value: ", self.value) 12 | llm = Subject().shared().llm 13 | noesis = "" 14 | if self.hint != None: 15 | noesis = self.value + f"({self.hint})" + "\n" 16 | else: 17 | noesis = self.value + "\n" 18 | display_var = "#"+self.id.replace("self.", "").upper()+":" 19 | llm += noesis 20 | llm += " Produce only the code, no example or explanation." + "\n" 21 | llm += display_var + " " + f" ```{self.__class__.__name__}\n" + gen(stop="```",name="response") + "\n" 22 | res = llm["response"] 23 | Subject().shared().llm += display_var + " " + res + "\n" 24 | self.noema = self.value 25 | self.value = res 26 | self.noesis = noesis 27 | if Subject().shared().verbose: 28 | print(f"{self.id.replace('self.', '')} = \033[93m{res}\033[0m (\033[94m{self.noema + f'({self.hint})'}\033[0m)") 29 | -------------------------------------------------------------------------------- /Noema/__init__.py: -------------------------------------------------------------------------------- 1 | from .Generator import Generator 2 | from .selectors import Select, SelectOrNone 3 | from .atomic_types import Int, Float, Bool, Date, DateTime, Time, Phone, Email, Word 4 | from .composed_types import ListOf 5 | from .information import Information 6 | from .substring import Substring 7 | from .text_gen import Sentence, Free, Paragraph 8 | from .programming_langugages import Python, Swift, Java, C, Cpp, CSharp, JavaScript, TypeScript, Ruby, PHP, Go, Rust, Kotlin, Dart, Scala, R, MATLAB, Julia, Lua, Perl, Shell, PowerShell, Bash, COBOL, Fortran, Assembly, Verilog, VHDL 9 | from .noesis_wrapper import Noema 10 | from .Subject import Subject 11 | from .reset import Reset 12 | from .semPy import SemPy 13 | 14 | __all__ = ['Generator', 'Select', 'SelectOrNone', 15 | 'Int', 'Float', 'Bool', 'Date', 16 | 'DateTime', 'Time', 'Phone', 'Email', 17 | 'Word', 'ListOf', 'Information', 'Sentence', 18 | 'Free', 'Paragraph', 'Noema', 'Subject', 'Reset', 19 | 'SemPy', 'Substring', 20 | 'Python', 'Swift', 'Java', 'C', 'Cpp', 'CSharp', 21 | 'JavaScript', 'TypeScript', 'Ruby', 'PHP', 'Go', 'Rust', 'Kotlin', 'Dart', 'Scala', 'R', 'MATLAB', 'Julia', 'Lua', 'Perl', 'Shell', 'PowerShell', 'Bash', 'COBOL', 'Fortran', 'Assembly', 'Verilog', 'VHDL'] -------------------------------------------------------------------------------- /flowchart.md: -------------------------------------------------------------------------------- 1 | ```mermaid 2 | graph TD 3 | A_client_want_to_create_a_blog._He_is_asking_for_SuperSociety_to_create_it. --> Conduct_a_thorough_research_on_popular_blogging_platforms_and_their_features_to_understand_the_client's_requirements_thoroughly. 4 | A_client_want_to_create_a_blog._He_is_asking_for_SuperSociety_to_create_it. --> Draft_a_detailed_proposal_outlining_the_platform_selection_criteria,_customization_options,_and_estimated_timeline_based_on_the_client's_needs. 5 | A_client_want_to_create_a_blog._He_is_asking_for_SuperSociety_to_create_it. --> Schedule_a_meeting_with_the_client_to_discuss_the_proposal,_gather_feedback,_and_finalize_the_blogging_platform_setup_plan. 6 | Conduct_a_thorough_research_on_popular_blogging_platforms_and_their_features_to_understand_the_client's_requirements_thoroughly. --> Identify_key_features_essential_for_a_blog_platform_based_on_client_needs_e. 7 | Conduct_a_thorough_research_on_popular_blogging_platforms_and_their_features_to_understand_the_client's_requirements_thoroughly. --> Analyze_top_blogging_platforms_WordPress,_Wix,_Squarespace_focusing_on_their_strengths_and_weaknesses. 8 | Conduct_a_thorough_research_on_popular_blogging_platforms_and_their_features_to_understand_the_client's_requirements_thoroughly. --> Compile_a_comparative_report_highlighting_suitability_for_client_requirements,_including_pricing_models_and_scalability_options. 9 | ``` -------------------------------------------------------------------------------- /Noema/atomic_types.py: -------------------------------------------------------------------------------- 1 | from .Generator import Generator 2 | 3 | class Word(Generator): 4 | regex = "[a-z]* | [A-Z][a-z]* | [a-z]+(_[a-z0-9]+)* | [a-z]+(.[a-z0-9]+)* | [a-z]+([A-Z][a-z0-9]*)*" 5 | hint = "Response format: a single word" 6 | return_type = str 7 | 8 | class Int(Generator): 9 | regex = "\d+$" 10 | hint = "Response format: Integer number" 11 | return_type = int 12 | 13 | class Float(Generator): 14 | regex = "\d+\.\d+$" 15 | hint = "Response format: Float number" 16 | return_type = float 17 | 18 | class Bool(Generator): 19 | regex = "(True|False)$" 20 | hint = "Response format: Boolean value" 21 | return_type = bool 22 | 23 | class Date(Generator): 24 | regex = "\d{4}-\d{2}-\d{2}$" 25 | hint = "Response format: YYYY-MM-DD" 26 | return_type = str 27 | 28 | class DateTime(Generator): 29 | regex = "\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$" 30 | hint = "Response format: YYYY-MM-DD HH:MM:SS" 31 | return_type = str 32 | 33 | class Time(Generator): 34 | regex = "\d{2}:\d{2}:\d{2}$" 35 | hint = "Response format: HH:MM:SS" 36 | return_type = str 37 | 38 | class Phone(Generator): 39 | regex = "\d{10}$" 40 | hint = "Response format: 10 digits" 41 | return_type = str 42 | 43 | class Email(Generator): 44 | regex = "[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*@[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*\.[a-zA-Z]+$" 45 | return_type = str 46 | hint = "Response format: email address" -------------------------------------------------------------------------------- /Noema/text_gen.py: -------------------------------------------------------------------------------- 1 | from .Generator import Generator 2 | from .Subject import Subject 3 | from guidance import gen 4 | 5 | class Sentence(Generator): 6 | regex = "[A-ZÀÂÄÉÈÊËÎÏÔŒÙÛÜÇ]?[a-zA-ZÀÂÄÉÈÊËÎÏÔŒÙÛÜÇàâäéèêëîïôœùûüç0-9\s,;:<>\{\}/=\-\+\%\*`'\"\\(\\)\-…_\$]*[.!?]$" 7 | hint = "Response format: a sentence" 8 | return_type = str 9 | stops = ["\n"] 10 | 11 | class Paragraph(Generator): 12 | regex = "[A-ZÀÂÄÉÈÊËÎÏÔŒÙÛÜÇ]?[a-zA-ZÀÂÄÉÈÊËÎÏÔŒÙÛÜÇàâäéèêëîïôœùûüç0-9\s,;:<>\{\}/=\-\+\%\*`'\"\\(\\)\-\.…\\n_\$]*[.!?]$" 13 | hint = "Response format: a paragraph" 14 | return_type = str 15 | stops = ["\n"] 16 | 17 | class Free(Generator): 18 | regex = "" 19 | hint = "Response format: text" 20 | return_type = str 21 | 22 | def execute(self, max_tokens=500): 23 | llm = Subject().shared().llm 24 | noesis = "" 25 | if self.hint != None: 26 | noesis = self.value + f"({self.hint})" + "\n" 27 | else: 28 | noesis = self.value + "\n" 29 | display_var = "#"+self.id.replace("self.", "").upper()+":" 30 | llm += noesis 31 | llm += display_var + " " + gen(name="response",max_tokens=max_tokens) + "\n" 32 | res = llm["response"] 33 | Subject().shared().llm += display_var + " " + res + "\n" 34 | self.noema = self.value 35 | self.value = res 36 | self.noesis = noesis 37 | Subject().shared().append_to_chain({"value": self.value, "noema": self.noema, "noesis": self.noesis}) 38 | if Subject().shared().verbose: 39 | print(f"{self.id.replace('self.', '')} = \033[93m{res}\033[0m (\033[94m{self.noema + f'({self.hint})'}\033[0m)") 40 | -------------------------------------------------------------------------------- /examples/basics.py: -------------------------------------------------------------------------------- 1 | from Noema import * 2 | 3 | # Create a subject (LLM) 4 | Subject("../Models/Mistral-NeMo-Minitron-8B-Instruct.Q4_K_M.gguf", verbose=True) # Llama cpp model 5 | 6 | # Create a way of thinking 7 | class SimpleWayOfThinking: 8 | 9 | def __init__(self, task): 10 | super().__init__() 11 | self.task = task 12 | 13 | @Noema 14 | def think(self): 15 | """ 16 | You are a simple thinker. You have a task to perform. 17 | Always looking for the best way to perform it. 18 | """ 19 | povs = [] 20 | task = Information(f"{self.task}") # inject information to the LLM 21 | for i in range(2): 22 | step_nb = i + 1 23 | reflexion = Sentence("Providing a reflexion about the task.", step_nb) 24 | consequence = Sentence("Providing the consequence of the reflexion.", step_nb) 25 | evaluate = Sentence("Evaluating the consequence.", step_nb) 26 | point_of_view = Sentence(f"Providing a point of view about the task different than {povs}", step_nb) 27 | point_of_view_qualification = Word(f"Qualifying the point of view, must choose a word different of: {povs}", step_nb) 28 | povs.append(point_of_view_qualification.value) 29 | creativitity_level = Float(f"How creative is this point of view: {povs[-1]}. (Between 0-10)", step_nb) 30 | if creativitity_level.value < 8.0: 31 | important = Information("I need to be more creative!") 32 | conclusion = Paragraph("Providing a conclusion which is a synthesis of the previous steps.") 33 | return conclusion.value # return the conclusion value 34 | 35 | 36 | swot = SimpleWayOfThinking("How to write a good iOS application?") 37 | conclusion = swot.think() 38 | print(conclusion) 39 | print(Subject().shared().noema()) 40 | 41 | -------------------------------------------------------------------------------- /examples/vizualisation.py: -------------------------------------------------------------------------------- 1 | from Noema import * 2 | 3 | # Create a new Subject 4 | Subject("../Models/granite-3.1-3b-a800m-instruct-Q4_K_M.gguf", verbose=True, write_graph=True) 5 | 6 | @Noema 7 | def analysis_evaluation(analysis): 8 | """ 9 | You are a specialist of analysis evaluation. 10 | You produce a numerical evaluation of the analysis, 0 is bad, 10 is good. 11 | Good means that the analysis is relevant and useful. 12 | Bad means that the analysis is not relevant and not useful. 13 | """ 14 | analysis_to_evaluate = Information(f"{analysis}") 15 | evaluation = Float("Evaluation of the analysis, between 0 and 10") 16 | return evaluation.value 17 | 18 | @Noema 19 | def comment_note_evaluation(analysis): 20 | """ 21 | You are a specialist of evaluation commenting. 22 | You always produce a deep analysis of the comment. 23 | """ 24 | analysis_to_evaluate = Information(f"{analysis}") 25 | comment = Sentence("Commenting the analysis") 26 | return comment.value 27 | 28 | @Noema 29 | def comment_evaluation(comment): 30 | """ 31 | You are a specialist of comment analysis. 32 | You always produce a deep analysis of the comment. 33 | """ 34 | comment_to_analyse = Information(f"{comment}") 35 | specialists = ["Psychologist", "Sociologist", "Linguist", "Philosopher"] 36 | analyse_by_specialists = {} 37 | for specialist in specialists: 38 | analysis = Sentence(f"Analysing the comment as a {specialist}") 39 | analyse_by_specialists[specialist] = analysis.value 40 | evaluation = analysis_evaluation(analysis.value) 41 | comment_note_evaluation_res = comment_note_evaluation(evaluation) 42 | improvements = ListOf(Sentence, "List 4 improvements") 43 | 44 | synthesis = Paragraph("Providing a synthesis of the analysis.") 45 | sub = Substring(f"Extracting synthesis comment from {synthesis.value}") 46 | print(sub.value) 47 | return synthesis.value 48 | 49 | synthesis = comment_evaluation("This llm is very good!") 50 | print(synthesis) 51 | -------------------------------------------------------------------------------- /Noema/composed_types.py: -------------------------------------------------------------------------------- 1 | import varname 2 | from .BaseGenerator import BaseGenerator 3 | from .Generator import noema_generator 4 | from .Subject import Subject 5 | from guidance import gen, select 6 | 7 | @noema_generator 8 | class ListOf(BaseGenerator): 9 | regex = None 10 | return_type = list 11 | hint = "Response format: a list of #ITEM_TYPE# separated by carriage returns." 12 | stops = [] 13 | 14 | def __init__(self, type=None, value=None, idx:int = None, var: str = None): 15 | super().__init__() 16 | self.type = type 17 | self.var = var 18 | self.value = value 19 | self.idx = idx 20 | 21 | def execute(self): 22 | llm = Subject().shared().llm 23 | noesis = "" 24 | local_hint = "" 25 | if self.hint != None: 26 | local_hint = self.hint.replace("#ITEM_TYPE#", self.type.__name__) 27 | noesis = self.value + f"({local_hint})" + "\n" 28 | else: 29 | noesis = self.value + "\n" 30 | var = "" 31 | display_var = "" 32 | if self.idx != None: 33 | var = self.id.replace("self.", "").upper()+f"_{self.idx}" 34 | else: 35 | var = self.id.replace("self.", "").upper() 36 | display_var = "#"+f"{var}:" 37 | llm += noesis 38 | 39 | item = self.type() 40 | res = [] 41 | llm += display_var + "\n```\n" 42 | first = True 43 | for i in range(100): 44 | if first: 45 | llm += f"{i+1}. " + gen(name="response", regex=item.regex, stop=f"\n{i+2}.") + "\n" 46 | value = llm["response"].strip() 47 | first = False 48 | else: 49 | llm += select([f"{i+1}. " + gen(name="response", regex=item.regex, stop=f"\n{i+2}.") + "\n", "```"], name="selected") 50 | value = llm["selected"].strip() 51 | if value == "```": 52 | break 53 | value = value.replace(f"{i+1}. ", "", 1) 54 | res.append(value) 55 | 56 | res_str = "\n".join(res) 57 | Subject().shared().llm += display_var + " " + res_str + "\n" 58 | self.noema = self.value 59 | self.value = res 60 | self.noesis = noesis 61 | Subject().shared().append_to_chain({"value": self.value, "noema": self.noema, "noesis": self.noesis}) 62 | if Subject().shared().verbose: 63 | print(f"{var} = \033[93m{res}\033[0m (\033[94m{self.noema + f'({local_hint})'}\033[0m)") 64 | -------------------------------------------------------------------------------- /Noema/selectors.py: -------------------------------------------------------------------------------- 1 | from .Generator import Generator 2 | from .Subject import Subject 3 | from guidance import select 4 | 5 | class Select(Generator): 6 | 7 | hint = "Response format: select the best option" 8 | 9 | def execute(self): 10 | llm = Subject().shared().llm 11 | noesis = "" 12 | if self.hint != None: 13 | noesis = self.value + f"({self.hint})" + "\n" 14 | else: 15 | noesis = self.value + "\n" 16 | 17 | var = "" 18 | display_var = "" 19 | if self.idx != None: 20 | var = self.id.replace("self.", "").upper()+f"_{self.idx}" 21 | else: 22 | var = self.id.replace("self.", "").upper() 23 | display_var = "#"+f"{var}:" 24 | llm += noesis 25 | llm += display_var + " " + select(self.options,name='response') + "\n" 26 | res = llm["response"] 27 | Subject().shared().llm += display_var + " " + res + "\n" 28 | self.noema = self.value 29 | self.value = res 30 | self.noesis = noesis 31 | Subject().shared().append_to_chain({"value": self.value, "noema": self.noema, "noesis": self.noesis}) 32 | if Subject().shared().verbose: 33 | print(f"{var} = \033[93m{res}\033[0m (\033[94m{self.noema + f'({self.hint} : {self.options})'}\033[0m)") 34 | 35 | 36 | class SelectOrNone(Generator): 37 | 38 | hint = "Response format: select the best option or 'None'" 39 | 40 | def execute(self): 41 | if "None" not in self.options: 42 | self.options.append("None") 43 | llm = Subject().shared().llm 44 | noesis = "" 45 | if self.hint != None: 46 | noesis = self.value + f"({self.hint})" + "\n" 47 | else: 48 | noesis = self.value + "\n" 49 | 50 | var = "" 51 | display_var = "" 52 | if self.idx != None: 53 | var = self.id.replace("self.", "").upper()+f"_{self.idx}" 54 | else: 55 | var = self.id.replace("self.", "").upper() 56 | display_var = "#"+f"{var}:" 57 | llm += noesis 58 | llm += display_var + " " + select(self.options,name='response') + "\n" 59 | res = llm["response"] 60 | Subject().shared().llm += display_var + " " + res + "\n" 61 | self.noema = self.value 62 | if res == "None": 63 | self.value = None 64 | else: 65 | self.value = res 66 | self.noesis = noesis 67 | Subject().shared().append_to_chain({"value": self.value, "noema": self.noema, "noesis": self.noesis}) 68 | if Subject().shared().verbose: 69 | print(f"{var} = \033[93m{res}\033[0m (\033[94m{self.noema + f'({self.hint} : {self.options})'}\033[0m)") 70 | -------------------------------------------------------------------------------- /Noema/Generator.py: -------------------------------------------------------------------------------- 1 | from .BaseGenerator import BaseGenerator 2 | from guidance import gen, select, substring 3 | from .Subject import Subject 4 | from varname import varname 5 | 6 | def noema_generator(cls): 7 | class Wrapped(cls): 8 | def __init__(self, *args, **kwargs): 9 | super().__init__(*args, **kwargs) 10 | original_class_type = self.__class__ 11 | self.id = varname() 12 | self.id = self.id.replace("self.", "") 13 | if hasattr(self, 'var') and self.var is not None: 14 | self._value = f"#{self.var.upper()}:" 15 | elif hasattr(self, 'value') and self.value is not None: 16 | self.execute() 17 | 18 | def __str__(self): 19 | return self._value if hasattr(self, '_value') else super().__str__() 20 | return Wrapped 21 | 22 | @noema_generator 23 | class Generator(BaseGenerator): 24 | regex = None 25 | return_type = None 26 | hint = None 27 | stops = [] 28 | 29 | def __init__(self, value=None, idx:int = None, var: str = None, options: list = None): 30 | super().__init__() 31 | self.var = var 32 | self.value = value 33 | self.idx = idx 34 | self.options = options 35 | 36 | def execute(self): 37 | llm = Subject().shared().llm 38 | noesis = "" 39 | if self.hint != None: 40 | noesis = self.value + f"({self.hint})" + "\n" 41 | else: 42 | noesis = self.value + "\n" 43 | var = "" 44 | display_var = "" 45 | if self.idx != None: 46 | var = self.id.replace("self.", "").upper()+f"_{self.idx}" 47 | else: 48 | var = self.id.replace("self.", "").upper() 49 | display_var = "#"+f"{var}:" 50 | llm += noesis 51 | 52 | if self.regex == "": 53 | llm += display_var + " " + gen(name="response") + "\n" 54 | else: 55 | llm += display_var + " " + gen(regex=self.regex, stop=self.stops, name="response") + "\n" 56 | res = llm["response"] 57 | Subject().shared().llm += display_var + " " + res + "\n" 58 | self.noema = self.value 59 | if self.return_type == bool: 60 | self.value = True if res == "True" else False 61 | else: 62 | self.value = self.return_type(res) 63 | self.noesis = noesis 64 | Subject().shared().append_to_chain({"value": self.value, "noema": self.noema, "noesis": self.noesis}) 65 | if Subject().shared().verbose: 66 | print(f"{var} = \033[93m{res}\033[0m (\033[94m{self.noema + f'({self.hint})'}\033[0m)") 67 | 68 | 69 | 70 | # TODO: Implement Fill class 71 | # class Fill(Generator): 72 | # regex = "" 73 | 74 | # def __init__(self, header, body, value=None): 75 | # super().__init__(value) 76 | # self.header = header 77 | # self.body = body 78 | 79 | 80 | -------------------------------------------------------------------------------- /Noema/semPy.py: -------------------------------------------------------------------------------- 1 | import ast 2 | from guidance import gen 3 | from .Generator import Generator 4 | from .Subject import Subject 5 | 6 | class SemPy(Generator): 7 | hint = "Response format: A Python function." 8 | 9 | def __init__(self, value, max_retry=3): 10 | self.noesis = value 11 | self.instruction = value 12 | self.value = None 13 | self.max_retry = max_retry 14 | self.id = "Python_Code" 15 | 16 | def run(self, *args, **kwargs): 17 | local_context = {} 18 | print("Value: ", self.value) 19 | tree = ast.parse(self.value) 20 | 21 | func_name = None 22 | for node in ast.walk(tree): 23 | if isinstance(node, ast.FunctionDef): 24 | func_name = node.name 25 | 26 | exec(self.value, local_context) 27 | 28 | print("Function name: ", func_name) 29 | print("Args: ", args) 30 | result = local_context[func_name](*args) 31 | return result 32 | 33 | def format_parameters(self, args, kwargs): 34 | # Formatter args 35 | formatted_args = ", ".join(repr(arg) for arg in args) 36 | 37 | # Formatter kwargs 38 | formatted_kwargs = ", ".join(f"{key}={repr(value)}" for key, value in kwargs.items()) 39 | 40 | # Combiner args et kwargs 41 | if formatted_args and formatted_kwargs: 42 | return f"{formatted_args}, {formatted_kwargs}" 43 | elif formatted_args: 44 | return formatted_args 45 | elif formatted_kwargs: 46 | return formatted_kwargs 47 | else: 48 | return "" 49 | 50 | def __call__(self, *args, **kwargs): 51 | formated_params = self.format_parameters(args, kwargs) 52 | llm = Subject().shared().llm 53 | self.noesis = f"""[INST]Generate a Python function to perform the following task: 54 | {self.instruction} 55 | 56 | Using the following form: 57 | ```Python 58 | # import libraries if needed 59 | 60 | def function_name({formated_params}): 61 | # Your code here 62 | 63 | 64 | # this function will be called by the Noema engine 65 | # do not change the name or the signature 66 | # it is the last thing you should write in your code. 67 | def noema_func({formated_params}): 68 | return function_name({formated_params}) 69 | ``` 70 | 71 | Always use the function name `noema_func` as the last function in your code to return the result to the Noema engine. 72 | Produce only the code, no example or explanation. 73 | [/INST] 74 | 75 | ```Python 76 | """ 77 | llm += self.noesis 78 | llm += gen(name="response", stop="```") 79 | function_str = llm["response"] 80 | self.noema = function_str 81 | local_context = {} 82 | print(function_str) 83 | exec(function_str, local_context) 84 | self.value = local_context["noema_func"](*args, **kwargs) 85 | Subject().shared().append_to_chain({"value": self.value, "noema": self.noema, "noesis": self.noesis}) 86 | if Subject().shared().verbose: 87 | print(f"\033[93m{self.noema}\n Returns:\n{self.value}\nFor parametters:{formated_params}\033[0m\n(\033[94m{self.noesis + f'({self.hint})'}\033[0m)") 88 | return self 89 | 90 | 91 | -------------------------------------------------------------------------------- /Noema/programming_langugages.py: -------------------------------------------------------------------------------- 1 | from .code_gen import CodeGenerator 2 | 3 | class Python(CodeGenerator): 4 | hint = "Response format: Python code" 5 | 6 | class Java(CodeGenerator): 7 | hint = "Response format: Java code" 8 | 9 | class C(CodeGenerator): 10 | hint = "Response format: C code" 11 | 12 | class Cpp(CodeGenerator): 13 | hint = "Response format: C++ code" 14 | 15 | class CSharp(CodeGenerator): 16 | hint = "Response format: C# code" 17 | 18 | class JavaScript(CodeGenerator): 19 | hint = "Response format: JavaScript code" 20 | 21 | class TypeScript(CodeGenerator): 22 | hint = "Response format: TypeScript code" 23 | 24 | class HTML(CodeGenerator): 25 | hint = "Response format: HTML code" 26 | 27 | class CSS(CodeGenerator): 28 | hint = "Response format: CSS code" 29 | 30 | class SQL(CodeGenerator): 31 | hint = "Response format: SQL code" 32 | 33 | class NoSQL(CodeGenerator): 34 | hint = "Response format: NoSQL query" 35 | 36 | class GraphQL(CodeGenerator): 37 | hint = "Response format: GraphQL query" 38 | 39 | class Rust(CodeGenerator): 40 | hint = "Response format: Rust code" 41 | 42 | class Go(CodeGenerator): 43 | hint = "Response format: Go code" 44 | 45 | class Ruby(CodeGenerator): 46 | hint = "Response format: Ruby code" 47 | 48 | class PHP(CodeGenerator): 49 | hint = "Response format: PHP code" 50 | 51 | class Shell(CodeGenerator): 52 | hint = "Response format: Shell script" 53 | 54 | class Bash(CodeGenerator): 55 | hint = "Response format: Bash script" 56 | 57 | class PowerShell(CodeGenerator): 58 | hint = "Response format: PowerShell script" 59 | 60 | class Perl(CodeGenerator): 61 | hint = "Response format: Perl code" 62 | 63 | class Lua(CodeGenerator): 64 | hint = "Response format: Lua code" 65 | 66 | class R(CodeGenerator): 67 | hint = "Response format: R code" 68 | 69 | class Scala(CodeGenerator): 70 | hint = "Response format: Scala code" 71 | 72 | class Kotlin(CodeGenerator): 73 | hint = "Response format: Kotlin code" 74 | 75 | class Dart(CodeGenerator): 76 | hint = "Response format: Dart code" 77 | 78 | class Swift(CodeGenerator): 79 | hint = "Response format: Swift code" 80 | 81 | class ObjectiveC(CodeGenerator): 82 | hint = "Response format: Objective-C code" 83 | 84 | class Assembly(CodeGenerator): 85 | hint = "Response format: Assembly code" 86 | 87 | class VHDL(CodeGenerator): 88 | hint = "Response format: VHDL code" 89 | 90 | class Verilog(CodeGenerator): 91 | hint = "Response format: Verilog code" 92 | 93 | class SystemVerilog(CodeGenerator): 94 | hint = "Response format: SystemVerilog code" 95 | 96 | class Julia(CodeGenerator): 97 | hint = "Response format: Julia code" 98 | 99 | class MATLAB(CodeGenerator): 100 | hint = "Response format: MATLAB code" 101 | 102 | class COBOL(CodeGenerator): 103 | hint = "Response format: COBOL code" 104 | 105 | class Fortran(CodeGenerator): 106 | hint = "Response format: Fortran code" 107 | 108 | class Ada(CodeGenerator): 109 | hint = "Response format: Ada code" 110 | 111 | class Pascal(CodeGenerator): 112 | hint = "Response format: Pascal code" 113 | 114 | class Lisp(CodeGenerator): 115 | hint = "Response format: Lisp code" 116 | 117 | class Prolog(CodeGenerator): 118 | hint = "Response format: Prolog code" 119 | 120 | class Smalltalk(CodeGenerator): 121 | hint = "Response format: Smalltalk code" 122 | 123 | class APL(CodeGenerator): 124 | hint = "Response format: APL code" -------------------------------------------------------------------------------- /Noema/noesis_wrapper.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | from functools import wraps 3 | import ast 4 | import re 5 | import textwrap 6 | from .Subject import * 7 | from .information import * 8 | from .selectors import * 9 | from .text_gen import * 10 | from .atomic_types import * 11 | from .substring import * 12 | from .composed_types import * 13 | from .semPy import * 14 | 15 | 16 | class ClassInstanceFinder(ast.NodeVisitor): 17 | def __init__(self, classes): 18 | self.classes = classes 19 | self.instances = [] 20 | 21 | def visit_Assign(self, node): 22 | if isinstance(node.value, ast.Call): 23 | class_name = self.get_class_name(node.value.func) 24 | if class_name in self.classes: 25 | var_names = [self.get_name(t) for t in node.targets] 26 | args = self.get_call_args(node.value) 27 | self.instances.append({'variables': var_names, 28 | 'class': class_name, 29 | 'args': args}) 30 | self.generic_visit(node) 31 | 32 | def get_class_name(self, func): 33 | if isinstance(func, ast.Name): 34 | return func.id 35 | elif isinstance(func, ast.Attribute): 36 | return func.attr 37 | return None 38 | 39 | def get_name(self, target): 40 | if isinstance(target, ast.Name): 41 | return target.id 42 | elif isinstance(target, ast.Attribute): 43 | return self.get_attribute_name(target) 44 | else: 45 | return None 46 | 47 | def get_attribute_name(self, attr): 48 | if isinstance(attr.value, ast.Name): 49 | return f"{attr.value.id}.{attr.attr}" 50 | elif isinstance(attr.value, ast.Attribute): 51 | return f"{self.get_attribute_name(attr.value)}.{attr.attr}" 52 | else: 53 | return attr.attr 54 | 55 | def get_call_args(self, call_node): 56 | args = [ast.literal_eval(arg) if isinstance(arg, ast.Constant) else ast.unparse(arg) 57 | for arg in call_node.args] 58 | kwargs = {kw.arg: ast.literal_eval(kw.value) if isinstance(kw.value, ast.Constant) else ast.unparse(kw.value) 59 | for kw in call_node.keywords} 60 | return {'args': args, 'kwargs': kwargs} 61 | 62 | class NoesisBuilder: 63 | 64 | def __init__(self,doc_string, instances): 65 | self.doc_string = doc_string 66 | self.instances = instances 67 | 68 | def build(self): 69 | noesis = f"[INST]{self.doc_string}\n" 70 | 71 | for instance in self.instances: 72 | class_name = instance["class"] 73 | instance_class = globals()[class_name] 74 | hint = "" 75 | if instance_class.hint != None: 76 | hint = instance_class.hint 77 | if len(instance['variables']) > 1: 78 | raise ValueError("Multiple variables not supported") 79 | if len(instance['variables']) == 1: 80 | if len(instance['args']['args']) > 0: 81 | step_name = instance['variables'][0].replace("self.", "").upper() 82 | noesis += "\n#"+step_name + " : " 83 | if class_name == "ListOf": 84 | value = instance['args']['args'][1] 85 | value = re.sub(r"\{.*?\}", "", value) 86 | noesis += value + " (" + hint.replace("#ITEM_TYPE#",instance['args']['args'][0]+"s") + ")" 87 | else: 88 | value = instance['args']['args'][0] 89 | value = re.sub(r"\{.*?\}", "", value) 90 | noesis += value 91 | if instance_class.hint != None: 92 | noesis += " (" + hint + ")" 93 | 94 | return noesis+ "\n[/INST]\n\n" 95 | 96 | def Noema(func): 97 | @wraps(func) 98 | def wrapper(*args, **kwargs): 99 | 100 | def find_subclasses(base_class, namespace): 101 | subclasses = [] 102 | for name, obj in namespace.items(): 103 | if isinstance(obj, type) and issubclass(obj, base_class) and obj is not base_class: 104 | subclasses.append((name, obj)) 105 | return subclasses 106 | 107 | func_name = func.__name__ 108 | doc = func.__doc__ 109 | if doc is None: 110 | raise ValueError("Noema function must have a docstring") 111 | source_code = inspect.getsource(func) 112 | source_code = textwrap.dedent(source_code) 113 | # TODO: Add dynamic class loading 114 | classes_to_find = ['Generator', 'Sentence', 'Email', 'Paragraph', 115 | 'Name', 'Address', 'Phone', 'Date', 'Time', 116 | 'Number', 'Select', 'SelectOrNone', 'Substring', 117 | 'Information', 'Free', 'ListOf', 'SemPy'] 118 | tree = ast.parse(source_code) 119 | finder = ClassInstanceFinder(classes_to_find) 120 | finder.visit(tree) 121 | noesis = NoesisBuilder(doc, finder.instances).build() 122 | Subject().shared().llm += "\n"+noesis 123 | Subject().shared().enter_function(func_name, doc, noesis) 124 | result = None # Initialisation de 'result' 125 | try: 126 | result = func(*args, **kwargs) 127 | except Exception as e: 128 | Subject().shared().exit_function(result) 129 | raise e # Relancer l'exception après le nettoyage 130 | else: 131 | Subject().shared().exit_function(result) 132 | return result 133 | 134 | return wrapper 135 | 136 | 137 | -------------------------------------------------------------------------------- /Noema/cfg.py: -------------------------------------------------------------------------------- 1 | from guidance import guidance, select, one_or_more, zero_or_more, optional, Tool 2 | 3 | class G(): 4 | 5 | def __init__(self) -> None: 6 | pass 7 | 8 | @guidance(stateless=True) 9 | def alphaNumPunct(lm): 10 | return lm + select(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 11 | 'é', 'è', 'ê', 'ë', 'à', 'â', 'ô', 'î', 'ï', 'û', 'ü', 'ç', 'æ', 'œ', 12 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 13 | 'É', 'È', 'Ê', 'Ë', 'À', 'Â', 'Ô', 'Î', 'Ï', 'Û', 'Ü', 'Ç', 'Æ', 'Œ', 14 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ', "'", ",", ".", ":", ";", "!", "?", "(", ")", "[", "]", "{", "}", "&", "-", "_", "+", "=", "*", "<", ">", "|", "@", "#", "$", "%", "^", "~", "`", "\\","/"]) 15 | 16 | @guidance(stateless=True) 17 | def alphaNumPunctForParagraph(lm): 18 | return lm + select(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 19 | 'é', 'è', 'ê', 'ë', 'à', 'â', 'ô', 'î', 'ï', 'û', 'ü', 'ç', 'æ', 'œ', 20 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 21 | 'É', 'È', 'Ê', 'Ë', 'À', 'Â', 'Ô', 'Î', 'Ï', 'Û', 'Ü', 'Ç', 'Æ', 'Œ', 22 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ', "'", ",", ".", ":", ";", "!", "?", "(", ")", "[", "]", "{", "}", "&", "-", "_", "+", "=", "*", "<", ">", "|", "@", "#", "$", "%", "^", "~", "`","\n"]) 23 | 24 | 25 | @guidance(stateless=True) 26 | def alphaNumPunctForSentence(lm): 27 | return lm + select(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 28 | 'é', 'è', 'ê', 'ë', 'à', 'â', 'ô', 'î', 'ï', 'û', 'ü', 'ç', 'æ', 'œ', 29 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 30 | 'É', 'È', 'Ê', 'Ë', 'À', 'Â', 'Ô', 'Î', 'Ï', 'Û', 'Ü', 'Ç', 'Æ', 'Œ', 31 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ', "'", ",", ":", ";", "!", "?", "(", ")", "[", "]", "{", "}", "&", "-", "_", "+", "=", "*", "<", ">", "|", "@", "#", "$", "%", "^", "~", "`"]) 32 | @guidance(stateless=True) 33 | def alphaNumPunctForSentenceInArray(lm): 34 | return lm + select(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 35 | 'é', 'è', 'ê', 'ë', 'à', 'â', 'ô', 'î', 'ï', 'û', 'ü', 'ç', 'æ', 'œ', 36 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 37 | 'É', 'È', 'Ê', 'Ë', 'À', 'Â', 'Ô', 'Î', 'Ï', 'Û', 'Ü', 'Ç', 'Æ', 'Œ', 38 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ', "'", ",", ":", ";", "!", "?", "(", ")", "{", "}", "&", "-", "_", "+", "=", "*", "<", ">", "|", "@", "#", "$", "%", "^", "~", "`"]) 39 | 40 | 41 | @guidance(stateless=True) 42 | def alphaNum(lm): 43 | return lm + select(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 44 | 'é', 'è', 'ê', 'ë', 'à', 'â', 'ô', 'î', 'ï', 'û', 'ü', 'ç', 'æ', 'œ', 45 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 46 | 'É', 'È', 'Ê', 'Ë', 'À', 'Â', 'Ô', 'Î', 'Ï', 'Û', 'Ü', 'Ç', 'Æ', 'Œ', 47 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']) 48 | 49 | @guidance(stateless=True) 50 | def alpha(lm): 51 | return lm + select(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 52 | 'é', 'è', 'ê', 'ë', 'à', 'â', 'ô', 'î', 'ï', 'û', 'ü', 'ç', 'æ', 'œ', 53 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 54 | 'É', 'È', 'Ê', 'Ë', 'À', 'Â', 'Ô', 'Î', 'Ï', 'Û', 'Ü', 'Ç', 'Æ', 'Œ']) 55 | 56 | @guidance(stateless=True) 57 | def alphaNumSep(lm): 58 | return lm + select(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 59 | 'é', 'è', 'ê', 'ë', 'à', 'â', 'ô', 'î', 'ï', 'û', 'ü', 'ç', 'æ', 'œ', 60 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 61 | 'É', 'È', 'Ê', 'Ë', 'À', 'Â', 'Ô', 'Î', 'Ï', 'Û', 'Ü', 'Ç', 'Æ', 'Œ', 62 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',"-","_"]) 63 | 64 | @guidance(stateless=True) 65 | def num(lm): 66 | return lm + one_or_more(select(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9','-'])) 67 | 68 | @guidance(stateless=True) 69 | def float(lm): 70 | return lm + one_or_more(select(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9','.','-'])) 71 | 72 | @guidance(stateless=True) 73 | def positive_num(lm): 74 | return lm + one_or_more(select(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'])) 75 | 76 | @guidance(stateless=True) 77 | def positive_float(lm): 78 | return lm + one_or_more(select(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9','.'])) 79 | 80 | @guidance(stateless=True) 81 | def bool(lm): 82 | return lm + select(['yes', 'no']) 83 | 84 | @guidance(stateless=True) 85 | def word(lm): 86 | return lm + one_or_more(G.alphaNumSep()) 87 | 88 | @guidance(stateless=True) 89 | def sentence(lm): 90 | return lm + one_or_more(G.alphaNumPunctForSentence())+"." 91 | 92 | @guidance(stateless=True) 93 | def free(lm): 94 | return lm + one_or_more(G.alphaNumPunctForParagraph())+"." 95 | 96 | @guidance(stateless=True) 97 | def number(lm): 98 | return lm + one_or_more(G.num()) 99 | 100 | @guidance(stateless=True) 101 | def bracket(lm): 102 | return lm + select(['["', '"]']) 103 | 104 | @guidance(stateless=True) 105 | def doubleQuote(lm): 106 | return lm + select(['"','", "','","']) 107 | 108 | @guidance(stateless=True) 109 | def element(lm, elementType): 110 | return lm + one_or_more(select([elementType, G.doubleQuote()])) 111 | 112 | @guidance(stateless=True) 113 | def arrayOf(lm, elementType): 114 | return lm + "[\"" + G.element(elementType) + G.bracket() 115 | 116 | @guidance(stateless=True) 117 | def elmSeparatedBy(lm,elementType,separator): 118 | return lm + one_or_more(select([elementType, select(separator)])) 119 | 120 | @guidance(stateless=True) 121 | def elmBetween(lm,elementsTypeBetween, elementType): 122 | return lm + "'" + elementType + "'" + select(elementsTypeBetween) + "'" + elementType + "'" 123 | 124 | @guidance(stateless=True) 125 | def email(lm): 126 | return lm + G.word() + "@" + G.word() + "." + G.word() 127 | 128 | @guidance(stateless=True) 129 | def url_http_https(lm): 130 | return lm + select(["http://", "https://"]) + G.word() + "." + one_or_more(G.alphaNumPunct()) -------------------------------------------------------------------------------- /Noema/Subject.py: -------------------------------------------------------------------------------- 1 | import json 2 | import textwrap 3 | from guidance import models,gen,select,capture 4 | 5 | class SingletonMeta(type): 6 | _instances = {} 7 | 8 | def __call__(cls, *args, **kwargs): 9 | if cls not in cls._instances: 10 | instance = super().__call__(*args, **kwargs) 11 | cls._instances[cls] = instance 12 | return cls._instances[cls] 13 | 14 | class Subject(metaclass=SingletonMeta): 15 | def __init__(self, model_path:str, context_size = 512*8, verbose = False, write_graph = False): 16 | self.verbose = verbose 17 | self.model_path = model_path 18 | self.write_graph = write_graph 19 | self.llm = models.LlamaCpp( 20 | self.model_path, 21 | n_gpu_layers=99, 22 | n_ctx=context_size, 23 | echo=False, 24 | ) 25 | self.structure = [] 26 | self.stack = [self.structure] 27 | 28 | def enter_function(self, f_name, inst, noesis): 29 | func = { 30 | "f_name": f_name, 31 | "inst": inst, 32 | "noesis": noesis, 33 | "chain": [] 34 | } 35 | self.stack[-1].append(func) 36 | self.stack.append(func["chain"]) 37 | 38 | def append_to_chain(self, value): 39 | step = value 40 | self.stack[-1].append(step) 41 | if self.write_graph: 42 | self.to_PlantUML_diagram() 43 | self.to_mermaid_diagram() 44 | 45 | def exit_function(self, return_value): 46 | if len(self.stack) > 1: 47 | self.stack.pop() 48 | self.stack[-1][-1]["return"] = return_value 49 | else: 50 | raise Exception("Aucune fonction à quitter.") 51 | 52 | def to_PlantUML_diagram(self): 53 | self.generate_plantuml_sequence(self.structure) 54 | 55 | def to_mermaid_diagram(self): 56 | self.json_to_mermaid_sequence(self.structure) 57 | 58 | def json_to_mermaid_sequence(self, json_data): 59 | """ 60 | Convertit un JSON structuré en un diagramme de séquence Mermaid. 61 | 62 | Args: 63 | json_data (list): Liste de dictionnaires représentant les fonctions et leurs chaînes. 64 | 65 | Returns: 66 | str: Chaîne contenant le code du diagramme de séquence Mermaid. 67 | """ 68 | mermaid = ['sequenceDiagram'] 69 | participants_added = set() 70 | 71 | def sanitize(text): 72 | """Échappe les caractères spéciaux pour Mermaid.""" 73 | if type(text) == list: 74 | text_str = "" 75 | for i in range(len(text)): 76 | t = str(text[i]) 77 | if len(t) > 50: 78 | t = textwrap.fill(t, width=50)+"
" 79 | text_str += t 80 | return text.replace("\n", "
").replace("'", "\\'").replace('"', '\\"') 81 | 82 | if type(text) != str: 83 | return text 84 | 85 | if len(text) > 50: 86 | text = textwrap.fill(text, width=50) 87 | 88 | return text.replace("\n", "
").replace("'", "\\'").replace('"', '\\"') 89 | 90 | def process_function(function_node, caller=None): 91 | f_name = function_node['f_name'] 92 | 93 | # Ajouter le participant si ce n'est pas déjà fait 94 | if f_name not in participants_added: 95 | mermaid.append(f"participant {f_name}") 96 | participants_added.add(f_name) 97 | 98 | # Ajouter une note au-dessus du participant avec 'inst' 99 | inst = sanitize(function_node['inst'].strip()) 100 | mermaid.append(f"Note over {f_name}: {inst}") 101 | 102 | for element in function_node.get('chain', []): 103 | if 'f_name' in element: 104 | sub_f_name = element['f_name'] 105 | 106 | if sub_f_name not in participants_added: 107 | mermaid.append(f"create participant {sub_f_name}") 108 | participants_added.add(sub_f_name) 109 | 110 | sub_inst = sanitize(element['inst'].strip()) 111 | mermaid.append(f"Note over {sub_f_name}: {sub_inst}") 112 | 113 | noesis = sanitize(element['noesis'].strip()) 114 | mermaid.append(f"Note right of {f_name}: {noesis}") 115 | 116 | mermaid.append(f"{f_name} ->> {sub_f_name}: {noesis}") 117 | 118 | process_function(element, caller=f_name) 119 | 120 | ret_val = sanitize(str(element.get('return', ''))) 121 | mermaid.append(f"{sub_f_name} -->> {f_name}: {ret_val}") 122 | else: 123 | noesis = sanitize(element.get('noesis', '').strip()) 124 | value = sanitize(str(element.get('value', '')).strip()) 125 | 126 | mermaid.append(f"Note right of {f_name}: {noesis}") 127 | 128 | mermaid.append(f"{f_name} ->> {f_name}: {value}") 129 | 130 | if caller and 'return' in function_node: 131 | ret_val = sanitize(str(function_node['return']).strip()) 132 | mermaid.append(f"{f_name} -->> {caller}: {ret_val}") 133 | 134 | for func in json_data: 135 | process_function(func) 136 | 137 | m = '\n'.join(mermaid) 138 | with open("diagram.mmd", "w") as f: 139 | f.write(m) 140 | 141 | 142 | def generate_plantuml_sequence(self, json_data, file_name="diagram.puml"): 143 | plantuml = ["@startuml", "!theme bluegray"] 144 | participants = set() 145 | 146 | def escape_note(text): 147 | 148 | if type(text) == list: 149 | text_str = "" 150 | for i in range(len(text)): 151 | t = str(text[i]) 152 | if len(t) > 50: 153 | t = textwrap.fill(t, width=50)+"\n" 154 | text_str += t 155 | return text_str.replace('"', '\\"').replace('\n', '\\n') 156 | 157 | if type(text) != str: 158 | return text 159 | 160 | if len(text) > 50: 161 | text = textwrap.fill(text, width=50) 162 | 163 | return text.replace('"', '\\"').replace('\n', '\\n') 164 | 165 | 166 | def process_function(func, parent=None): 167 | f_name = func.get("f_name") 168 | inst = func.get("inst", "").strip() 169 | noesis = func.get("noesis", "").strip() 170 | chain = func.get("chain", []) 171 | return_value = func.get("return") 172 | 173 | if f_name not in participants: 174 | plantuml.append(f"participant {f_name}") 175 | participants.add(f_name) 176 | 177 | if inst: 178 | plantuml.append(f"note over {f_name}: {escape_note(inst)}") 179 | 180 | current_actor = f_name 181 | 182 | for item in chain: 183 | if "f_name" in item: 184 | sub_f_name = item["f_name"] 185 | 186 | if sub_f_name not in participants: 187 | plantuml.append(f"create {sub_f_name}") 188 | participants.add(sub_f_name) 189 | plantuml.append(f"{current_actor} -> {sub_f_name}: Call {sub_f_name}") 190 | plantuml.append("group " + sub_f_name) 191 | sub_noesis = item.get("noesis", "").strip() 192 | if sub_noesis: 193 | plantuml.append(f"note over {sub_f_name}: {escape_note(sub_noesis)}") 194 | process_function(item, parent=current_actor) 195 | plantuml.append("end group") 196 | 197 | plantuml.append(f"{sub_f_name} --> {current_actor}: Return {escape_note(item.get('return'))}") 198 | current_actor = f_name 199 | else: 200 | value = item.get("value") 201 | noesis = item.get("noesis", "").strip() 202 | noema = item.get("noema", "").strip() 203 | 204 | if noesis: 205 | plantuml.append(f"note right of {current_actor}: {escape_note(noesis)}") 206 | 207 | plantuml.append(f"{current_actor} -> {current_actor}: {escape_note(value)}") 208 | 209 | for func in json_data: 210 | process_function(func) 211 | 212 | plantuml.append("@enduml") 213 | plantuml_str = "\n".join(plantuml) 214 | with open("diagram.puml", "w") as f: 215 | f.write(plantuml_str) 216 | 217 | @classmethod 218 | def shared(cls): 219 | if cls not in SingletonMeta._instances: 220 | raise Exception("You must instantiate the subject with a llm path.") 221 | return SingletonMeta._instances[cls] 222 | 223 | def noema(self): 224 | return str(self.llm) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2024 Alban Perli 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /diagram.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | !theme bluegray 3 | participant comment_evaluation 4 | note over comment_evaluation: You are a specialist of comment analysis. You\nalways produce a deep analysis of the comment. 5 | note right of comment_evaluation: #COMMENT_TO_ANALYSE: This llm is very good! 6 | comment_evaluation -> comment_evaluation: This llm is very good! 7 | note right of comment_evaluation: Analysing the comment as a Psychologist(Response\nformat: a sentence) 8 | comment_evaluation -> comment_evaluation: The comment expresses a positive sentiment towards\nthe llm, indicating satisfaction or approval. 9 | create analysis_evaluation 10 | comment_evaluation -> analysis_evaluation: Call analysis_evaluation 11 | group analysis_evaluation 12 | note over analysis_evaluation: [INST] You are a specialist of analysis\nevaluation. You produce a numerical evaluation\nof the analysis, 0 is bad, 10 is good. Good\nmeans that the analysis is relevant and useful.\nBad means that the analysis is not relevant and\nnot useful. #ANALYSIS_TO_EVALUATE :\nf'{analysis}' [/INST] 13 | note over analysis_evaluation: You are a specialist of analysis evaluation.\nYou produce a numerical evaluation of the\nanalysis, 0 is bad, 10 is good. Good means\nthat the analysis is relevant and useful. Bad\nmeans that the analysis is not relevant and not\nuseful. 14 | note right of analysis_evaluation: #ANALYSIS_TO_EVALUATE: The comment expresses a\npositive sentiment towards the llm, indicating\nsatisfaction or approval. 15 | analysis_evaluation -> analysis_evaluation: The comment expresses a positive sentiment towards\nthe llm, indicating satisfaction or approval. 16 | note right of analysis_evaluation: Evaluation of the analysis, between 0 and\n10(Response format: Float number) 17 | analysis_evaluation -> analysis_evaluation: 8.5 18 | end group 19 | analysis_evaluation --> comment_evaluation: Return 8.5 20 | create comment_note_evaluation 21 | comment_evaluation -> comment_note_evaluation: Call comment_note_evaluation 22 | group comment_note_evaluation 23 | note over comment_note_evaluation: [INST] You are a specialist of evaluation\ncommenting. You always produce a deep analysis\nof the comment. #ANALYSIS_TO_EVALUATE :\nf'{analysis}' #COMMENT : Commenting the analysis\n(Response format: a sentence) [/INST] 24 | note over comment_note_evaluation: You are a specialist of evaluation commenting.\nYou always produce a deep analysis of the comment. 25 | note right of comment_note_evaluation: #ANALYSIS_TO_EVALUATE: 8.5 26 | comment_note_evaluation -> comment_note_evaluation: 8.5 27 | note right of comment_note_evaluation: Commenting the analysis(Response format: a\nsentence) 28 | comment_note_evaluation -> comment_note_evaluation: The comment expresses a positive sentiment towards\nthe llm, indicating satisfaction or approval. 29 | end group 30 | comment_note_evaluation --> comment_evaluation: Return The comment expresses a positive sentiment towards\nthe llm, indicating satisfaction or approval. 31 | note right of comment_evaluation: List 4 improvements(Response format: a list of\nSentence separated by carriage returns.) 32 | comment_evaluation -> comment_evaluation: The comment could be improved by providing more\nspecific details about the llm.\nThe analysis could be enhanced by discussing the\nstrengths and weaknesses of the llm.\nThe comment could be more concise, focusing on the\nmain points.\nThe analysis could benefit from a more nuanced\nperspective, considering different aspects of the\nllm.\n 33 | note right of comment_evaluation: Analysing the comment as a Sociologist(Response\nformat: a sentence) 34 | comment_evaluation -> comment_evaluation: The comment reflects a sociological perspective,\nhighlighting the social impact and acceptance of\nthe llm. 35 | comment_evaluation -> analysis_evaluation: Call analysis_evaluation 36 | group analysis_evaluation 37 | note over analysis_evaluation: [INST] You are a specialist of analysis\nevaluation. You produce a numerical evaluation\nof the analysis, 0 is bad, 10 is good. Good\nmeans that the analysis is relevant and useful.\nBad means that the analysis is not relevant and\nnot useful. #ANALYSIS_TO_EVALUATE :\nf'{analysis}' [/INST] 38 | note over analysis_evaluation: You are a specialist of analysis evaluation.\nYou produce a numerical evaluation of the\nanalysis, 0 is bad, 10 is good. Good means\nthat the analysis is relevant and useful. Bad\nmeans that the analysis is not relevant and not\nuseful. 39 | note right of analysis_evaluation: #ANALYSIS_TO_EVALUATE: The comment reflects a\nsociological perspective, highlighting the social\nimpact and acceptance of the llm. 40 | analysis_evaluation -> analysis_evaluation: The comment reflects a sociological perspective,\nhighlighting the social impact and acceptance of\nthe llm. 41 | note right of analysis_evaluation: Evaluation of the analysis, between 0 and\n10(Response format: Float number) 42 | analysis_evaluation -> analysis_evaluation: 9.5 43 | end group 44 | analysis_evaluation --> comment_evaluation: Return 9.5 45 | comment_evaluation -> comment_note_evaluation: Call comment_note_evaluation 46 | group comment_note_evaluation 47 | note over comment_note_evaluation: [INST] You are a specialist of evaluation\ncommenting. You always produce a deep analysis\nof the comment. #ANALYSIS_TO_EVALUATE :\nf'{analysis}' #COMMENT : Commenting the analysis\n(Response format: a sentence) [/INST] 48 | note over comment_note_evaluation: You are a specialist of evaluation commenting.\nYou always produce a deep analysis of the comment. 49 | note right of comment_note_evaluation: #ANALYSIS_TO_EVALUATE: 9.5 50 | comment_note_evaluation -> comment_note_evaluation: 9.5 51 | note right of comment_note_evaluation: Commenting the analysis(Response format: a\nsentence) 52 | comment_note_evaluation -> comment_note_evaluation: The comment provides a comprehensive view of the\nllm's social impact and acceptance, supported by\nrelevant sociological insights. 53 | end group 54 | comment_note_evaluation --> comment_evaluation: Return The comment provides a comprehensive view of the\nllm's social impact and acceptance, supported by\nrelevant sociological insights. 55 | note right of comment_evaluation: List 4 improvements(Response format: a list of\nSentence separated by carriage returns.) 56 | comment_evaluation -> comment_evaluation: The comment could be more specific about the llm's\nfeatures and capabilities.\nThe analysis could benefit from a more balanced\nperspective, considering both positive and\nnegative aspects.\nThe comment could be more concise, focusing on the\nkey points.\nThe analysis could incorporate more recent\nresearch and data to provide a more up-to-date\nperspective.\n 57 | note right of comment_evaluation: Analysing the comment as a Linguist(Response\nformat: a sentence) 58 | comment_evaluation -> comment_evaluation: The comment demonstrates a strong understanding of\nthe llm's social implications, with a focus on its\nacceptance and impact. 59 | comment_evaluation -> analysis_evaluation: Call analysis_evaluation 60 | group analysis_evaluation 61 | note over analysis_evaluation: [INST] You are a specialist of analysis\nevaluation. You produce a numerical evaluation\nof the analysis, 0 is bad, 10 is good. Good\nmeans that the analysis is relevant and useful.\nBad means that the analysis is not relevant and\nnot useful. #ANALYSIS_TO_EVALUATE :\nf'{analysis}' [/INST] 62 | note over analysis_evaluation: You are a specialist of analysis evaluation.\nYou produce a numerical evaluation of the\nanalysis, 0 is bad, 10 is good. Good means\nthat the analysis is relevant and useful. Bad\nmeans that the analysis is not relevant and not\nuseful. 63 | note right of analysis_evaluation: #ANALYSIS_TO_EVALUATE: The comment demonstrates a\nstrong understanding of the llm's social\nimplications, with a focus on its acceptance and\nimpact. 64 | analysis_evaluation -> analysis_evaluation: The comment demonstrates a strong understanding of\nthe llm's social implications, with a focus on its\nacceptance and impact. 65 | note right of analysis_evaluation: Evaluation of the analysis, between 0 and\n10(Response format: Float number) 66 | analysis_evaluation -> analysis_evaluation: 9.0 67 | end group 68 | analysis_evaluation --> comment_evaluation: Return 9.0 69 | comment_evaluation -> comment_note_evaluation: Call comment_note_evaluation 70 | group comment_note_evaluation 71 | note over comment_note_evaluation: [INST] You are a specialist of evaluation\ncommenting. You always produce a deep analysis\nof the comment. #ANALYSIS_TO_EVALUATE :\nf'{analysis}' #COMMENT : Commenting the analysis\n(Response format: a sentence) [/INST] 72 | note over comment_note_evaluation: You are a specialist of evaluation commenting.\nYou always produce a deep analysis of the comment. 73 | note right of comment_note_evaluation: #ANALYSIS_TO_EVALUATE: 9.0 74 | comment_note_evaluation -> comment_note_evaluation: 9.0 75 | note right of comment_note_evaluation: Commenting the analysis(Response format: a\nsentence) 76 | comment_note_evaluation -> comment_note_evaluation: The comment provides a nuanced perspective on the\nllm's social implications, considering both its\nacceptance and impact. 77 | end group 78 | comment_note_evaluation --> comment_evaluation: Return The comment provides a nuanced perspective on the\nllm's social implications, considering both its\nacceptance and impact. 79 | note right of comment_evaluation: List 4 improvements(Response format: a list of\nSentence separated by carriage returns.) 80 | comment_evaluation -> comment_evaluation: The comment could be more specific about the llm's\nfeatures and capabilities.\nThe analysis could benefit from a more balanced\nperspective, considering both positive and\nnegative aspects.\nThe comment could be more concise, focusing on the\nkey points.\nThe analysis could incorporate more recent\nresearch and data to provide a more up-to-date\nperspective.\n 81 | note right of comment_evaluation: Analysing the comment as a Philosopher(Response\nformat: a sentence) 82 | comment_evaluation -> comment_evaluation: The comment reflects a philosophical approach,\nexamining the llm's social implications and\nacceptance from a broader perspective. 83 | comment_evaluation -> analysis_evaluation: Call analysis_evaluation 84 | group analysis_evaluation 85 | note over analysis_evaluation: [INST] You are a specialist of analysis\nevaluation. You produce a numerical evaluation\nof the analysis, 0 is bad, 10 is good. Good\nmeans that the analysis is relevant and useful.\nBad means that the analysis is not relevant and\nnot useful. #ANALYSIS_TO_EVALUATE :\nf'{analysis}' [/INST] 86 | note over analysis_evaluation: You are a specialist of analysis evaluation.\nYou produce a numerical evaluation of the\nanalysis, 0 is bad, 10 is good. Good means\nthat the analysis is relevant and useful. Bad\nmeans that the analysis is not relevant and not\nuseful. 87 | note right of analysis_evaluation: #ANALYSIS_TO_EVALUATE: The comment reflects a\nphilosophical approach, examining the llm's social\nimplications and acceptance from a broader\nperspective. 88 | analysis_evaluation -> analysis_evaluation: The comment reflects a philosophical approach,\nexamining the llm's social implications and\nacceptance from a broader perspective. 89 | note right of analysis_evaluation: Evaluation of the analysis, between 0 and\n10(Response format: Float number) 90 | analysis_evaluation -> analysis_evaluation: 8.5 91 | end group 92 | analysis_evaluation --> comment_evaluation: Return 8.5 93 | comment_evaluation -> comment_note_evaluation: Call comment_note_evaluation 94 | group comment_note_evaluation 95 | note over comment_note_evaluation: [INST] You are a specialist of evaluation\ncommenting. You always produce a deep analysis\nof the comment. #ANALYSIS_TO_EVALUATE :\nf'{analysis}' #COMMENT : Commenting the analysis\n(Response format: a sentence) [/INST] 96 | note over comment_note_evaluation: You are a specialist of evaluation commenting.\nYou always produce a deep analysis of the comment. 97 | note right of comment_note_evaluation: #ANALYSIS_TO_EVALUATE: 8.5 98 | comment_note_evaluation -> comment_note_evaluation: 8.5 99 | note right of comment_note_evaluation: Commenting the analysis(Response format: a\nsentence) 100 | comment_note_evaluation -> comment_note_evaluation: The comment offers a thoughtful exploration of the\nllm's social implications and acceptance,\nsupported by relevant philosophical insights. 101 | end group 102 | comment_note_evaluation --> comment_evaluation: Return The comment offers a thoughtful exploration of the\nllm's social implications and acceptance,\nsupported by relevant philosophical insights. 103 | note right of comment_evaluation: List 4 improvements(Response format: a list of\nSentence separated by carriage returns.) 104 | comment_evaluation -> comment_evaluation: The comment could be more specific about the llm's\nfeatures and capabilities.\nThe analysis could benefit from a more balanced\nperspective, considering both positive and\nnegative aspects.\nThe comment could be more concise, focusing on the\nkey points.\nThe analysis could incorporate more recent\nresearch and data to provide a more up-to-date\nperspective.\n 105 | note right of comment_evaluation: Providing a synthesis of the analysis.(Response\nformat: a paragraph) 106 | comment_evaluation -> comment_evaluation: The comment provides a nuanced exploration of the\nllm's social implications and acceptance,\nsupported by relevant philosophical insights. It\nhighlights the importance of considering both\npositive and negative aspects, and the need for a\nbalanced perspective. The comment could be more\nconcise and focused on the key points, and could\nbenefit from incorporating more recent research\nand data. 107 | note right of comment_evaluation: Extracting synthesis comment from The comment\nprovides a nuanced exploration of the llm's social\nimplications and acceptance, supported by relevant\nphilosophical insights. It highlights the\nimportance of considering both positive and\nnegative aspects, and the need for a balanced\nperspective. The comment could be more concise and\nfocused on the key points, and could benefit from\nincorporating more recent research and\ndata.(Response format: extract a substring) 108 | comment_evaluation -> comment_evaluation: The comment provides a nuanced exploration of the\nllm's social implications and acceptance,\nsupported by relevant philosophical insights. It\nhighlights the importance of considering both\npositive and negative aspects, and the need for a\nbalanced perspective. The comment could be more\nconcise and focused on the key points, and could\nbenefit from incorporating more recent research\nand data. 109 | @enduml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | ReadMe Banner 3 |

4 | 5 | 6 |
7 | Seamless integration between python and llm's generations. 8 |

9 | With Noema, you can control the model and choose the path it will follow. 10 |
This framework aims to enable developpers to use **LLM as a though interpretor**, not as a source of truth. 11 | 12 | Noema is built on [llamacpp](https://github.com/ggerganov/llama.cpp) and [guidance](https://github.com/guidance-ai/guidance)'s shoulders. 13 |

14 |
15 | 16 | ## Installation 17 | 18 | ```bash 19 | pip install Noema 20 | ``` 21 | 22 | Install [llama-cpp-python](https://github.com/abetlen/llama-cpp-python?tab=readme-ov-file#supported-backends) using the correct backend. 23 | 24 | 25 | 26 | # Basic: 27 | ```python 28 | from Noema import * 29 | 30 | # Create a subject (LLM) 31 | Subject("../Models/EXAONE-3.5-2.4B-Instruct-Q4_K_M.gguf", verbose=True) # Llama cpp model 32 | 33 | @Noema 34 | def think(task): 35 | """ 36 | You are a simple thinker. You have a task to perform. 37 | Always looking for the best way to perform it. 38 | """ 39 | povs = [] 40 | task = Information(f"{task}") # inject information to the LLM 41 | for i in range(4): 42 | step_nb = i + 1 43 | reflection = Sentence("Providing a reflection about the task.", step_nb) 44 | consequence = Sentence("Providing the consequence of the reflection.", step_nb) 45 | evaluate = Sentence("Evaluating the consequence.", step_nb) 46 | point_of_view = Sentence(f"Providing a point of view about the task different than {povs}", step_nb) 47 | point_of_view_qualification = Word(f"Qualifying the point of view, must choose a word different of: {povs}", step_nb) 48 | povs.append(point_of_view_qualification.value) 49 | creativitity_level = Float(f"How creative is this point of view: {povs[-1]}. (Between 0-10)", step_nb) 50 | if creativitity_level.value < 8.0: 51 | important = Information("I need to be more creative!") 52 | conclusion = Paragraph("Providing a conclusion which is a synthesis of the previous steps.") 53 | return conclusion.value # return the conclusion value 54 | 55 | 56 | conclusion = think("How to write a good iOS application?") 57 | print(conclusion) 58 | ``` 59 | 60 |
61 | LLM output: 62 | 63 | ``` 64 | [INST] 65 | You are a simple thinker. You have a task to perform. 66 | Always looking for the best way to perform it. 67 | 68 | #TASK : f'{self.task}' 69 | #REFLEXION : Providing a reflection about the task. (Response format: a sentence) 70 | #CONSEQUENCE : Providing the consequence of the reflection. (Response format: a sentence) 71 | #EVALUATE : Evaluating the consequence. (Response format: a sentence) 72 | #POINT_OF_VIEW : f'Providing a point of view about the task different than {povs}' (Response format: a sentence) 73 | #IMPORTANT : I need to be more creative! 74 | #CONCLUSION : Providing a conclusion which is a synthesis of the previous steps. (Response format: a paragraph) 75 | [/INST] 76 | 77 | #TASK: How to write a good iOS application? 78 | #REFLEXION_1: Writing an iOS app requires balancing user experience with technical feasibility, which can be daunting without a structured approach. 79 | #CONSEQUENCE_1: Without a clear roadmap, developers might overlook critical aspects like usability or performance optimization, leading to a subpar product. 80 | #EVALUATE_1: The consequence highlights the necessity for meticulous planning and iterative testing to ensure both functionality and user satisfaction. 81 | #POINT_OF_VIEW_1: Instead of focusing solely on coding, consider adopting a design-first approach where user stories guide the development process, ensuring alignment between vision and execution. 82 | #POINT_OF_VIEW_QUALIFICATION_1: Designer 83 | #CREATIVITITY_LEVEL_1: 7.5 84 | #IMPORTANT: I need to be more creative! 85 | #REFLEXION_2: The challenge lies in translating abstract design concepts into tangible iOS applications, necessitating a deep understanding of both user needs and platform capabilities. 86 | #CONSEQUENCE_2: Ignoring user feedback during development phases could result in an application that fails to meet market expectations and user expectations, potentially causing significant financial losses and damage to brand reputation. 87 | #EVALUATE_2: Ignoring user feedback during development phases could lead to a product that fails to resonate with its target audience, undermining both commercial success and user trust. 88 | #POINT_OF_VIEW_2: From a developer's perspective, integrating innovative features while maintaining robustness requires a blend of creativity and technical expertise, ensuring seamless integration of cutting-edge functionalities without compromising stability. 89 | #POINT_OF_VIEW_QUALIFICATION_2: Architect 90 | #CREATIVITITY_LEVEL_2: 8.2 91 | #CONCLUSION: Crafting a successful iOS application necessitates a multifaceted approach that harmonizes creativity with rigorous planning and iterative refinement. By adopting a design-first methodology and integrating user feedback throughout development, developers can navigate the complexities of balancing innovation with practicality, ultimately delivering applications that not only meet but exceed user expectations, thereby fostering both user satisfaction and commercial success. Emphasizing creativity alongside meticulous planning ensures that each aspect of the development process contributes meaningfully to the final product's success. 92 | ``` 93 | 94 |
95 | 96 | # Background: 97 | 98 | **Noema is an application of the [*declarative* programming](https://en.wikipedia.org/wiki/Declarative_programming) paradigm to a language model.** 99 | 100 | - [Concept](#Concept) 101 | - [Installation](#installation) 102 | - [Features](#features) 103 | 104 | 105 | ## Concept 106 | 107 | - **Noesis**: can be seen as the description of a function 108 | - **Noema**: is the representation (step by step) of this description 109 | - **Constitution**: is the process of transformation Noesis->Noema. 110 | - **Subject**: the object producing the Noema via the constitution of the noesis. Here, the LLM. 111 | 112 | **Noema**/**Noesis**, **Subject**, and **Constitution** are a pedantic and naive application of concept borrowed from [Husserl's phenomenology](https://en.wikipedia.org/wiki/Edmund_Husserl). 113 | 114 | 115 | ## ReAct prompting: 116 | We can use ReAct prompting with LLM. 117 | 118 | `ReAct` prompting is a powerful way for guiding a LLM. 119 | 120 | ### ReAct example: 121 | ```You are in a loop of though. 122 | Question: Here is the question 123 | Reflection: Thinking about the question 124 | Observation: Providing observation about the Reflection 125 | Analysis: Formulating an analysis about your current reflection 126 | Conclusion: Conclude by a synthesis of the reflection. 127 | 128 | Question: {user_input} 129 | Reflection: 130 | ``` 131 | 132 | In that case, the LLM will follow the provided steps: `Reflection,Observation,Analysis,Conclusion` 133 | 134 | `Thinking about the quesion` is the **Noesis** of `Reflection` 135 | 136 | The content *generated* by the LLM corresponding to `Reflection` is the **Noema**. 137 | 138 | ### Noema let you write python code that automagically: 139 | 1. Build the ReAct prompt 140 | 2. Let you intercepts (constrained) generations 141 | 3. Use it in standard python code 142 | 143 | ## Features 144 | 145 | ### Create the Subject 146 | 147 | ```python 148 | from Noema import * 149 | 150 | Subject("path/to/your/model.gguf", verbose=True) # Full Compatibiliy with LLamaCPP. 151 | ``` 152 | 153 | ### Create a way of thinking: 154 | 155 | #### 1. Add @Noema decorator to a function/method. 156 | ```python 157 | from Noema import * 158 | 159 | Subject("../Models/EXAONE-3.5-2.4B-Instruct-Q4_K_M.gguf", verbose=True) # Llama cpp model 160 | 161 | @Noema 162 | def comment_evaluation(comment): 163 | pass 164 | ``` 165 | #### 2. Add a system prompt using the python docstring 166 | ```python 167 | from Noema import * 168 | 169 | @Noema 170 | def comment_evaluation(comment): 171 | """ 172 | You are a specialist of comment analysis. 173 | You always produce a deep analysis of the comment. 174 | """ 175 | ``` 176 | #### 3. Write python code 177 | ```python 178 | from Noema import * 179 | 180 | @Noema 181 | def comment_evaluation(comment): 182 | """ 183 | You are a specialist of comment analysis. 184 | You always produce a deep analysis of the comment. 185 | """ 186 | comment_to_analyse = Information(f"{comment}") 187 | specialists = ["Psychologist", "Product manager", "Satisfaction manager"] 188 | analyse_by_specialists = {} 189 | for specialist in specialists: 190 | analysis = Sentence(f"Analysing the comment as a {specialist}") 191 | analyse_by_specialists[specialist] = analysis.value 192 | 193 | synthesis = Paragraph("Providing a synthesis of the analysis.") 194 | return synthesis.value, analyse_by_specialists 195 | 196 | Subject("../Models/EXAONE-3.5-2.4B-Instruct-Q4_K_M.gguf", verbose=True) # Llama cpp model 197 | 198 | synthesis, abs = comment_evaluation("This llm is very good at following instructions!") 199 | 200 | print(synthesis) 201 | ``` 202 | 203 |
204 | LLM output: 205 | [INST] 206 | 207 | You are a specialist of comment analysis. 208 | You always produce a deep analysis of the comment. 209 | 210 | 211 | #COMMENT_TO_ANALYSE : f'{comment}' 212 | 213 | #ANALYSIS : f'Analysing the comment as a {specialist}' (Response format: a sentence) 214 | 215 | #SYNTHESIS : Providing a synthesis of the analysis. (Response format: a paragraph) 216 | 217 | [/INST] 218 | 219 | #COMMENT_TO_ANALYSE: This llm is very good! 220 | 221 | #ANALYSIS: The comment expresses a positive sentiment towards the LLM's capabilities, suggesting satisfaction with its performance and possibly indicating a belief in its psychological sophistication or understanding of human interaction nuances. 222 | 223 | #ANALYSIS: As a product manager, this feedback highlights the importance of user satisfaction and perceived intelligence in LLM evaluations, indicating a focus on enhancing user experience through advanced functionalities and addressing potential psychological aspects beyond mere functionality. 224 | 225 | #ANALYSIS: The comment reflects high user satisfaction with the LLM's performance, emphasizing its perceived intelligence and nuanced understanding, which are critical factors for product managers aiming to meet user expectations and foster trust through advanced technological capabilities. 226 | 227 | #SYNTHESIS: The comment underscores a significant positive reception of the LLM, highlighting its perceived intelligence and nuanced understanding beyond basic functionality. This feedback is crucial for product managers as it underscores the importance of aligning technological advancements with user expectations for psychological satisfaction and trust-building. Addressing these aspects could enhance user engagement and satisfaction, positioning the LLM as a valuable asset in meeting evolving technological and psychological needs within its applications. Future iterations should focus on maintaining and potentially elevating these perceived qualities to further solidify its role as a sophisticated tool in diverse user contexts. 228 |
229 | 230 | ## Generators 231 | Generators are used to generate content from the subject (LLM) through the noesis (the task description). 232 | 233 | Generated value always have 3 properties: 234 | - var_name.value -> The generated value 235 | - var_name.noesis -> The instruction 236 | - var_name.noema -> The generated value 237 | 238 | They always produce the corresponding python type. 239 | 240 | ### Simple Generators 241 | 242 | | Noema Type | Python Type | Usage | 243 | |-----------|-----------|-----------| 244 | | Int | int | `number = Int("Give me a number between 0 and 10")` | 245 | | Float | float | `number = Float("Give me a number between 0.1 and 0.7")` | 246 | | Bool | bool | `truth:Bool = Bool("Are local LLMs better than online LLMs?")` | 247 | | Word | str | `better = Word("Which instruct LLM is the best?")` | 248 | | Sentence | str | `explaination = Sentence("Explain why")` | 249 | | Paragraph | str | `long_explaination = Paragraph("Give mode details")` | 250 | | Free | str | `unlimited = Free("Speak a lot without control...")` | 251 | 252 | 253 | ### Composed Generators 254 | 255 | List of simple Generators can be built. 256 | | Noema Type | Generator Type | Usage | 257 | |-----------|-----------|-----------| 258 | | ListOf | [Int] | `number = ListOf(Int,"Give me a list of number between 0 and 10")` | 259 | | ListOf | [Float] | `number = ListOf(Float,"Give me a list of number between 0.1 and 0.7")` | 260 | | ListOf | [Bool] | `truth_list = ListOf(Bool,"Are local LLMs better than online LLMs, and Mistral better than LLama?")` | 261 | | ListOf | [Word] | `better = ListOf(Word,"List the best instruct LLM")` | 262 | | ListOf | [Sentence] | `explaination = ListOf(Sentence,"Explain step by step why")` | 263 | 264 | 265 | ### Selectors 266 | 267 | Select the appropriate value. 268 | | Noema Type | Generator Type | Usage | 269 | |-----------|-----------|-----------| 270 | | Select | str | `qualify_synthesis = Select("Qualify the synthesis", options=["good", "bad", "neutral"])` | 271 | | SelectOrNone | str | `contains = SelectOrNone("Does contain the following ideas", options=["Need to update the code", "Is totally secured"])` | 272 | 273 | 274 | ### Code Generator 275 | 276 | The `LanguageName` type provide a way to generate `LanguageName` code 277 | 278 | | Noema Type | Python Type | Usage | 279 | |-----------|-----------|-----------| 280 | | Python | str | `interface = Python("With pyqt5, genereate a window with a text field and a OK button.")` | 281 | 282 |
283 | Language List 284 | 285 | - Python 286 | - Java 287 | - C 288 | - Cpp 289 | - CSharp 290 | - JavaScript 291 | - TypeScript 292 | - HTML 293 | - CSS 294 | - SQL 295 | - NoSQL 296 | - GraphQL 297 | - Rust 298 | - Go 299 | - Ruby 300 | - PHP 301 | - Shell 302 | - Bash 303 | - PowerShell 304 | - Perl 305 | - Lua 306 | - R 307 | - Scala 308 | - Kotlin 309 | - Dart 310 | - Swift 311 | - ObjectiveC 312 | - Assembly 313 | - VHDL 314 | - Verilog 315 | - SystemVerilog 316 | - Julia 317 | - MATLAB 318 | - COBOL 319 | - Fortran 320 | - Ada 321 | - Pascal 322 | - Lisp 323 | - Prolog 324 | - Smalltalk 325 | - APL 326 | 327 |
328 | 329 | 330 | 331 | ### Information 332 | 333 | The type Information is useful to insert some context to the LLM at the right time in the reflection process. 334 | 335 | | Noema Type | Python Type | Usage | 336 | |-----------|-----------|-----------| 337 | | Information | str | `tips = Information("Here you can inject some information in the LLM")` | 338 | 339 | Here we use a simple string, but we can also insert a string from a python function call, do some RAG or any other tasks. 340 | 341 | 342 | # Advanced: 343 | 344 | ### SemPy : Semantic python 345 | 346 | The SemPy type is creating Python function dynamically and execute it with your parameters. 347 | | Noema Type | Python Type | Usage | 348 | |-----------|-----------|-----------| 349 | | SemPy | depending | `letter_place = SemPy("Find the place of a letter in a word.")("hello world","o")` | 350 | 351 | ```python 352 | from Noema import * 353 | 354 | @Noema 355 | def simple_task(task, parameters): 356 | """You are an incredible Python developer. 357 | Always looking for the best way to write code.""" 358 | task_to_code = Information(f"I want to {task}") 359 | formulation = Sentence("Reformulate the task to be easily understood by a Python developer.") 360 | decomposition = ListOf(Sentence,"Decompose the task into smaller sub-tasks.") 361 | result = SemPy(formulation.value)(parameters) 362 | # Generated code: 363 | # 364 | # def function_name(word): 365 | # letter_counts = {} 366 | # for char in word: 367 | # if char.isalpha(): # Ensure only letters are counted 368 | # char = char.lower() # Convert to lowercase for uniformity 369 | # if char in letter_counts: 370 | # letter_counts[char] += 1 371 | # else: 372 | # letter_counts[char] = 1 373 | # return letter_counts 374 | 375 | # def noema_func(word): 376 | # return function_name(word) 377 | 378 | return result.value 379 | 380 | Subject("../Models/EXAONE-3.5-7.8B-Instruct-Q4_K_M.gguf",verbose=True) 381 | nb_letter = simple_task("Count the occurence of letters in a word", "strawberry") 382 | print(nb_letter) 383 | # {'s': 1, 't': 1, 'r': 3, 'a': 1, 'w': 1, 'b': 1, 'e': 1, 'y': 1} 384 | ``` 385 | 386 | ### Visualization 387 | 388 | Enabling reflection visualization with `write_graph = True` in the Subject init create a PlantUML and Mermaid diagram respectively in `diagram.puml` and `diagram.mmd` 389 | 390 | ```python 391 | from Noema import * 392 | 393 | 394 | # Create a new Subject 395 | Subject("../Models/granite-3.1-3b-a800m-instruct-Q4_K_M.gguf", verbose=True, write_graph=True) 396 | 397 | @Noema 398 | def analysis_evaluation(analysis): 399 | """ 400 | You are a specialist of analysis evaluation. 401 | You produce a numerical evaluation of the analysis, 0 is bad, 10 is good. 402 | Good means that the analysis is relevant and useful. 403 | Bad means that the analysis is not relevant and not useful. 404 | """ 405 | analysis_to_evaluate = Information(f"{analysis}") 406 | evaluation = Float("Evaluation of the analysis, between 0 and 10") 407 | return evaluation.value 408 | 409 | @Noema 410 | def comment_note_evaluation(analysis): 411 | """ 412 | You are a specialist of evaluation commenting. 413 | You always produce a deep analysis of the comment. 414 | """ 415 | analysis_to_evaluate = Information(f"{analysis}") 416 | comment = Sentence("Commenting the analysis") 417 | return comment.value 418 | 419 | @Noema 420 | def comment_evaluation(comment): 421 | """ 422 | You are a specialist of comment analysis. 423 | You always produce a deep analysis of the comment. 424 | """ 425 | comment_to_analyse = Information(f"{comment}") 426 | specialists = ["Psychologist", "Sociologist", "Linguist", "Philosopher"] 427 | analyse_by_specialists = {} 428 | for specialist in specialists: 429 | analysis = Sentence(f"Analysing the comment as a {specialist}") 430 | analyse_by_specialists[specialist] = analysis.value 431 | evaluation = analysis_evaluation(analysis.value) 432 | comment_note_evaluation_res = comment_note_evaluation(evaluation) 433 | improvements = ListOf(Sentence, "List 4 improvements") 434 | 435 | synthesis = Paragraph("Providing a synthesis of the analysis.") 436 | sub = Substring(f"Extracting synthesis comment from {synthesis.value}") 437 | print(sub.value) 438 | return synthesis.value 439 | 440 | synthesis = comment_evaluation("This llm is very good!") 441 | print(synthesis) 442 | ``` 443 |

444 | Visualization example 445 |

446 | 447 | 448 | -------------------------------------------------------------------------------- /diagram.mmd: -------------------------------------------------------------------------------- 1 | sequenceDiagram 2 | participant comment_evaluation 3 | Note over comment_evaluation: You are a specialist of comment analysis. You
always produce a deep analysis of the comment. 4 | Note right of comment_evaluation: #COMMENT_TO_ANALYSE: This llm is very good! 5 | comment_evaluation ->> comment_evaluation: This llm is very good! 6 | Note right of comment_evaluation: Analysing the comment as a Psychologist(Response
format: a sentence) 7 | comment_evaluation ->> comment_evaluation: The comment expresses a positive sentiment towards
the llm, indicating satisfaction or approval. 8 | create participant analysis_evaluation 9 | Note over analysis_evaluation: You are a specialist of analysis evaluation.
You produce a numerical evaluation of the
analysis, 0 is bad, 10 is good. Good means
that the analysis is relevant and useful. Bad
means that the analysis is not relevant and not
useful. 10 | Note right of comment_evaluation: [INST] You are a specialist of analysis
evaluation. You produce a numerical evaluation
of the analysis, 0 is bad, 10 is good. Good
means that the analysis is relevant and useful.
Bad means that the analysis is not relevant and
not useful. #ANALYSIS_TO_EVALUATE :
f\'{analysis}\' [/INST] 11 | comment_evaluation ->> analysis_evaluation: [INST] You are a specialist of analysis
evaluation. You produce a numerical evaluation
of the analysis, 0 is bad, 10 is good. Good
means that the analysis is relevant and useful.
Bad means that the analysis is not relevant and
not useful. #ANALYSIS_TO_EVALUATE :
f\'{analysis}\' [/INST] 12 | Note over analysis_evaluation: You are a specialist of analysis evaluation.
You produce a numerical evaluation of the
analysis, 0 is bad, 10 is good. Good means
that the analysis is relevant and useful. Bad
means that the analysis is not relevant and not
useful. 13 | Note right of analysis_evaluation: #ANALYSIS_TO_EVALUATE: The comment expresses a
positive sentiment towards the llm, indicating
satisfaction or approval. 14 | analysis_evaluation ->> analysis_evaluation: The comment expresses a positive sentiment towards
the llm, indicating satisfaction or approval. 15 | Note right of analysis_evaluation: Evaluation of the analysis, between 0 and
10(Response format: Float number) 16 | analysis_evaluation ->> analysis_evaluation: 8.5 17 | analysis_evaluation -->> comment_evaluation: 8.5 18 | analysis_evaluation -->> comment_evaluation: 8.5 19 | create participant comment_note_evaluation 20 | Note over comment_note_evaluation: You are a specialist of evaluation commenting.
You always produce a deep analysis of the comment. 21 | Note right of comment_evaluation: [INST] You are a specialist of evaluation
commenting. You always produce a deep analysis
of the comment. #ANALYSIS_TO_EVALUATE :
f\'{analysis}\' #COMMENT : Commenting the analysis
(Response format: a sentence) [/INST] 22 | comment_evaluation ->> comment_note_evaluation: [INST] You are a specialist of evaluation
commenting. You always produce a deep analysis
of the comment. #ANALYSIS_TO_EVALUATE :
f\'{analysis}\' #COMMENT : Commenting the analysis
(Response format: a sentence) [/INST] 23 | Note over comment_note_evaluation: You are a specialist of evaluation commenting.
You always produce a deep analysis of the comment. 24 | Note right of comment_note_evaluation: #ANALYSIS_TO_EVALUATE: 8.5 25 | comment_note_evaluation ->> comment_note_evaluation: 8.5 26 | Note right of comment_note_evaluation: Commenting the analysis(Response format: a
sentence) 27 | comment_note_evaluation ->> comment_note_evaluation: The comment expresses a positive sentiment towards
the llm, indicating satisfaction or approval. 28 | comment_note_evaluation -->> comment_evaluation: The comment expresses a positive sentiment towards
the llm, indicating satisfaction or approval. 29 | comment_note_evaluation -->> comment_evaluation: The comment expresses a positive sentiment towards
the llm, indicating satisfaction or approval. 30 | Note right of comment_evaluation: List 4 improvements(Response format: a list of
Sentence separated by carriage returns.) 31 | comment_evaluation ->> comment_evaluation: [\'The comment could be improved by providing more
specific details about the llm.\', \'The analysis
could be enhanced by discussing the strengths and
weaknesses of the llm.\', \'The comment could be
more concise, focusing on the main points.\', \'The
analysis could benefit from a more nuanced
perspective, considering different aspects of the
llm.\'] 32 | Note right of comment_evaluation: Analysing the comment as a Sociologist(Response
format: a sentence) 33 | comment_evaluation ->> comment_evaluation: The comment reflects a sociological perspective,
highlighting the social impact and acceptance of
the llm. 34 | Note over analysis_evaluation: You are a specialist of analysis evaluation.
You produce a numerical evaluation of the
analysis, 0 is bad, 10 is good. Good means
that the analysis is relevant and useful. Bad
means that the analysis is not relevant and not
useful. 35 | Note right of comment_evaluation: [INST] You are a specialist of analysis
evaluation. You produce a numerical evaluation
of the analysis, 0 is bad, 10 is good. Good
means that the analysis is relevant and useful.
Bad means that the analysis is not relevant and
not useful. #ANALYSIS_TO_EVALUATE :
f\'{analysis}\' [/INST] 36 | comment_evaluation ->> analysis_evaluation: [INST] You are a specialist of analysis
evaluation. You produce a numerical evaluation
of the analysis, 0 is bad, 10 is good. Good
means that the analysis is relevant and useful.
Bad means that the analysis is not relevant and
not useful. #ANALYSIS_TO_EVALUATE :
f\'{analysis}\' [/INST] 37 | Note over analysis_evaluation: You are a specialist of analysis evaluation.
You produce a numerical evaluation of the
analysis, 0 is bad, 10 is good. Good means
that the analysis is relevant and useful. Bad
means that the analysis is not relevant and not
useful. 38 | Note right of analysis_evaluation: #ANALYSIS_TO_EVALUATE: The comment reflects a
sociological perspective, highlighting the social
impact and acceptance of the llm. 39 | analysis_evaluation ->> analysis_evaluation: The comment reflects a sociological perspective,
highlighting the social impact and acceptance of
the llm. 40 | Note right of analysis_evaluation: Evaluation of the analysis, between 0 and
10(Response format: Float number) 41 | analysis_evaluation ->> analysis_evaluation: 9.5 42 | analysis_evaluation -->> comment_evaluation: 9.5 43 | analysis_evaluation -->> comment_evaluation: 9.5 44 | Note over comment_note_evaluation: You are a specialist of evaluation commenting.
You always produce a deep analysis of the comment. 45 | Note right of comment_evaluation: [INST] You are a specialist of evaluation
commenting. You always produce a deep analysis
of the comment. #ANALYSIS_TO_EVALUATE :
f\'{analysis}\' #COMMENT : Commenting the analysis
(Response format: a sentence) [/INST] 46 | comment_evaluation ->> comment_note_evaluation: [INST] You are a specialist of evaluation
commenting. You always produce a deep analysis
of the comment. #ANALYSIS_TO_EVALUATE :
f\'{analysis}\' #COMMENT : Commenting the analysis
(Response format: a sentence) [/INST] 47 | Note over comment_note_evaluation: You are a specialist of evaluation commenting.
You always produce a deep analysis of the comment. 48 | Note right of comment_note_evaluation: #ANALYSIS_TO_EVALUATE: 9.5 49 | comment_note_evaluation ->> comment_note_evaluation: 9.5 50 | Note right of comment_note_evaluation: Commenting the analysis(Response format: a
sentence) 51 | comment_note_evaluation ->> comment_note_evaluation: The comment provides a comprehensive view of the
llm\'s social impact and acceptance, supported by
relevant sociological insights. 52 | comment_note_evaluation -->> comment_evaluation: The comment provides a comprehensive view of the
llm\'s social impact and acceptance, supported by
relevant sociological insights. 53 | comment_note_evaluation -->> comment_evaluation: The comment provides a comprehensive view of the
llm\'s social impact and acceptance, supported by
relevant sociological insights. 54 | Note right of comment_evaluation: List 4 improvements(Response format: a list of
Sentence separated by carriage returns.) 55 | comment_evaluation ->> comment_evaluation: [\"The comment could be more specific about the
llm\'s features and capabilities.\", \'The analysis
could benefit from a more balanced perspective,
considering both positive and negative aspects.\',
\'The comment could be more concise, focusing on
the key points.\', \'The analysis could incorporate
more recent research and data to provide a more
up-to-date perspective.\'] 56 | Note right of comment_evaluation: Analysing the comment as a Linguist(Response
format: a sentence) 57 | comment_evaluation ->> comment_evaluation: The comment demonstrates a strong understanding of
the llm\'s social implications, with a focus on its
acceptance and impact. 58 | Note over analysis_evaluation: You are a specialist of analysis evaluation.
You produce a numerical evaluation of the
analysis, 0 is bad, 10 is good. Good means
that the analysis is relevant and useful. Bad
means that the analysis is not relevant and not
useful. 59 | Note right of comment_evaluation: [INST] You are a specialist of analysis
evaluation. You produce a numerical evaluation
of the analysis, 0 is bad, 10 is good. Good
means that the analysis is relevant and useful.
Bad means that the analysis is not relevant and
not useful. #ANALYSIS_TO_EVALUATE :
f\'{analysis}\' [/INST] 60 | comment_evaluation ->> analysis_evaluation: [INST] You are a specialist of analysis
evaluation. You produce a numerical evaluation
of the analysis, 0 is bad, 10 is good. Good
means that the analysis is relevant and useful.
Bad means that the analysis is not relevant and
not useful. #ANALYSIS_TO_EVALUATE :
f\'{analysis}\' [/INST] 61 | Note over analysis_evaluation: You are a specialist of analysis evaluation.
You produce a numerical evaluation of the
analysis, 0 is bad, 10 is good. Good means
that the analysis is relevant and useful. Bad
means that the analysis is not relevant and not
useful. 62 | Note right of analysis_evaluation: #ANALYSIS_TO_EVALUATE: The comment demonstrates a
strong understanding of the llm\'s social
implications, with a focus on its acceptance and
impact. 63 | analysis_evaluation ->> analysis_evaluation: The comment demonstrates a strong understanding of
the llm\'s social implications, with a focus on its
acceptance and impact. 64 | Note right of analysis_evaluation: Evaluation of the analysis, between 0 and
10(Response format: Float number) 65 | analysis_evaluation ->> analysis_evaluation: 9.0 66 | analysis_evaluation -->> comment_evaluation: 9.0 67 | analysis_evaluation -->> comment_evaluation: 9.0 68 | Note over comment_note_evaluation: You are a specialist of evaluation commenting.
You always produce a deep analysis of the comment. 69 | Note right of comment_evaluation: [INST] You are a specialist of evaluation
commenting. You always produce a deep analysis
of the comment. #ANALYSIS_TO_EVALUATE :
f\'{analysis}\' #COMMENT : Commenting the analysis
(Response format: a sentence) [/INST] 70 | comment_evaluation ->> comment_note_evaluation: [INST] You are a specialist of evaluation
commenting. You always produce a deep analysis
of the comment. #ANALYSIS_TO_EVALUATE :
f\'{analysis}\' #COMMENT : Commenting the analysis
(Response format: a sentence) [/INST] 71 | Note over comment_note_evaluation: You are a specialist of evaluation commenting.
You always produce a deep analysis of the comment. 72 | Note right of comment_note_evaluation: #ANALYSIS_TO_EVALUATE: 9.0 73 | comment_note_evaluation ->> comment_note_evaluation: 9.0 74 | Note right of comment_note_evaluation: Commenting the analysis(Response format: a
sentence) 75 | comment_note_evaluation ->> comment_note_evaluation: The comment provides a nuanced perspective on the
llm\'s social implications, considering both its
acceptance and impact. 76 | comment_note_evaluation -->> comment_evaluation: The comment provides a nuanced perspective on the
llm\'s social implications, considering both its
acceptance and impact. 77 | comment_note_evaluation -->> comment_evaluation: The comment provides a nuanced perspective on the
llm\'s social implications, considering both its
acceptance and impact. 78 | Note right of comment_evaluation: List 4 improvements(Response format: a list of
Sentence separated by carriage returns.) 79 | comment_evaluation ->> comment_evaluation: [\"The comment could be more specific about the
llm\'s features and capabilities.\", \'The analysis
could benefit from a more balanced perspective,
considering both positive and negative aspects.\',
\'The comment could be more concise, focusing on
the key points.\', \'The analysis could incorporate
more recent research and data to provide a more
up-to-date perspective.\'] 80 | Note right of comment_evaluation: Analysing the comment as a Philosopher(Response
format: a sentence) 81 | comment_evaluation ->> comment_evaluation: The comment reflects a philosophical approach,
examining the llm\'s social implications and
acceptance from a broader perspective. 82 | Note over analysis_evaluation: You are a specialist of analysis evaluation.
You produce a numerical evaluation of the
analysis, 0 is bad, 10 is good. Good means
that the analysis is relevant and useful. Bad
means that the analysis is not relevant and not
useful. 83 | Note right of comment_evaluation: [INST] You are a specialist of analysis
evaluation. You produce a numerical evaluation
of the analysis, 0 is bad, 10 is good. Good
means that the analysis is relevant and useful.
Bad means that the analysis is not relevant and
not useful. #ANALYSIS_TO_EVALUATE :
f\'{analysis}\' [/INST] 84 | comment_evaluation ->> analysis_evaluation: [INST] You are a specialist of analysis
evaluation. You produce a numerical evaluation
of the analysis, 0 is bad, 10 is good. Good
means that the analysis is relevant and useful.
Bad means that the analysis is not relevant and
not useful. #ANALYSIS_TO_EVALUATE :
f\'{analysis}\' [/INST] 85 | Note over analysis_evaluation: You are a specialist of analysis evaluation.
You produce a numerical evaluation of the
analysis, 0 is bad, 10 is good. Good means
that the analysis is relevant and useful. Bad
means that the analysis is not relevant and not
useful. 86 | Note right of analysis_evaluation: #ANALYSIS_TO_EVALUATE: The comment reflects a
philosophical approach, examining the llm\'s social
implications and acceptance from a broader
perspective. 87 | analysis_evaluation ->> analysis_evaluation: The comment reflects a philosophical approach,
examining the llm\'s social implications and
acceptance from a broader perspective. 88 | Note right of analysis_evaluation: Evaluation of the analysis, between 0 and
10(Response format: Float number) 89 | analysis_evaluation ->> analysis_evaluation: 8.5 90 | analysis_evaluation -->> comment_evaluation: 8.5 91 | analysis_evaluation -->> comment_evaluation: 8.5 92 | Note over comment_note_evaluation: You are a specialist of evaluation commenting.
You always produce a deep analysis of the comment. 93 | Note right of comment_evaluation: [INST] You are a specialist of evaluation
commenting. You always produce a deep analysis
of the comment. #ANALYSIS_TO_EVALUATE :
f\'{analysis}\' #COMMENT : Commenting the analysis
(Response format: a sentence) [/INST] 94 | comment_evaluation ->> comment_note_evaluation: [INST] You are a specialist of evaluation
commenting. You always produce a deep analysis
of the comment. #ANALYSIS_TO_EVALUATE :
f\'{analysis}\' #COMMENT : Commenting the analysis
(Response format: a sentence) [/INST] 95 | Note over comment_note_evaluation: You are a specialist of evaluation commenting.
You always produce a deep analysis of the comment. 96 | Note right of comment_note_evaluation: #ANALYSIS_TO_EVALUATE: 8.5 97 | comment_note_evaluation ->> comment_note_evaluation: 8.5 98 | Note right of comment_note_evaluation: Commenting the analysis(Response format: a
sentence) 99 | comment_note_evaluation ->> comment_note_evaluation: The comment offers a thoughtful exploration of the
llm\'s social implications and acceptance,
supported by relevant philosophical insights. 100 | comment_note_evaluation -->> comment_evaluation: The comment offers a thoughtful exploration of the
llm\'s social implications and acceptance,
supported by relevant philosophical insights. 101 | comment_note_evaluation -->> comment_evaluation: The comment offers a thoughtful exploration of the
llm\'s social implications and acceptance,
supported by relevant philosophical insights. 102 | Note right of comment_evaluation: List 4 improvements(Response format: a list of
Sentence separated by carriage returns.) 103 | comment_evaluation ->> comment_evaluation: [\"The comment could be more specific about the
llm\'s features and capabilities.\", \'The analysis
could benefit from a more balanced perspective,
considering both positive and negative aspects.\',
\'The comment could be more concise, focusing on
the key points.\', \'The analysis could incorporate
more recent research and data to provide a more
up-to-date perspective.\'] 104 | Note right of comment_evaluation: Providing a synthesis of the analysis.(Response
format: a paragraph) 105 | comment_evaluation ->> comment_evaluation: The comment provides a nuanced exploration of the
llm\'s social implications and acceptance,
supported by relevant philosophical insights. It
highlights the importance of considering both
positive and negative aspects, and the need for a
balanced perspective. The comment could be more
concise and focused on the key points, and could
benefit from incorporating more recent research
and data. 106 | Note right of comment_evaluation: Extracting synthesis comment from The comment
provides a nuanced exploration of the llm\'s social
implications and acceptance, supported by relevant
philosophical insights. It highlights the
importance of considering both positive and
negative aspects, and the need for a balanced
perspective. The comment could be more concise and
focused on the key points, and could benefit from
incorporating more recent research and
data.(Response format: extract a substring) 107 | comment_evaluation ->> comment_evaluation: The comment provides a nuanced exploration of the
llm\'s social implications and acceptance,
supported by relevant philosophical insights. It
highlights the importance of considering both
positive and negative aspects, and the need for a
balanced perspective. The comment could be more
concise and focused on the key points, and could
benefit from incorporating more recent research
and data. --------------------------------------------------------------------------------