Repository: hand-e-fr/OpenHosta (language: python)

Issue:
Prompts could be shorter and optimized.
Do a list of elements given to the LLM at the start
Faire référence à la fonction elle-même avec son nom dans le user prompt
ajouter des "```" pour les bloques techniques
[ADD] docs for example

The function `example` can add example(s) for multiple executive function
- [X] Create example function
- [X] example can be specified outside a function
- [x] example add in hostacache


Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 4c589c6..d3730eb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 __pycache__/
+__hostacache__/
 *.pyc
 *.pyo
 .env/
@@ -8,3 +9,7 @@ __pycache__/
 *.mmd
 .openhosta
 dist/*
+__hostacache__/
+.coverage
+*.html
+.pytest_cache
\ No newline at end of file
diff --git a/CHAGELOG.md b/CHAGELOG.md
index a2d990b..fab5203 100644
--- a/CHAGELOG.md
+++ b/CHAGELOG.md
@@ -4,6 +4,39 @@ All significant changes to this project will be documented in this file.
 
 ---
 
+## **v1.1**
+
+- **Fixes**
+  - the `emulate` function is now decorator-resistant.
+  - The function `config.set_default_model` works now w/ `config.set_default_apiKey`
+  - `thought` function now accept multiple arguments
+  - `suggest` and `analytics` call now th LLM with `Model` class (`ai_call`)
+  - `emulate` works now in a nested function.
+  
+- **Enhancement**
+  - the `thought` function guess automatically his return type with LLM
+  - `suggest` return now his data returned by LLM in a dict
+  - `emulate` works now for class methods
+  - `emulate` now integrate locals variable of emulated function to the LLM prompt.
+  - `_hostacache_` are now available for function infos storage
+  - Added support for the **typing** module: You can now use specific return types from the typing module, including [List, Dict, Tuple, Set, FrozenSet, Deque, Iterable, Sequence, Mapping, Union, Optional, Literal].
+  - `emulate` now includes a verification output that checks and converts the output to the specified type if necessary. Supported types include **Pydantic** models, all types from the **typing** module mentioned above, as well as built-in types such as `dict`, `int`, `float`, `str`, `list`, `set`, `frozenset`, `tuple`, and `bool`.
+    - The `complex` type is not supported. If you need to use complex numbers, please pass them as a `tuple` and manually convert them after processing.
+  -  The return type predicted by `thought` is now attached as an attribute to the object created: `_return_type`.
+
+- **Features**
+  - `example` function that can add some example for a specified hosta-injected function (inside or outside it) and add it to the cache
+  - `save_examples` function that can save in a JSONL file all he example of an hosta-injected function
+  - `load_examples` function that can load an example file an a cache for an hosta-injected function
+  - `set_prompt` in `PromptManager` enables to change automatically a prompt un "prompt.json"
+
+- **Optimization**
+  - `emulate` prompt changed: confidence level removed: (~20% speed gained) (`emulate`: v1.1)
+  - All prompt enhanced: added code block, separator and reference to the name of the processed function (`emulate`: v1.2, `enhance` v1.2, `thought`: v1.1, `estimate`: v1.1)
+
+- **Internal**
+  - Functional tests have been added for each of the library's features
+
 ## **v1.0** 29/08/2024:
 
 - **Features**
diff --git a/Makefile b/Makefile
index ea3b5c8..b5c7c9a 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 PACKAGE_NAME = OpenHosta
 SRC_DIR = src/OpenHosta
 TEST_DIR = tests
-DOC_DIR = Documentation
+DOC_DIR = doc
 
 all: help
 
@@ -9,25 +9,23 @@ help:
 	@echo Usage:
 	@echo 	make help             Show this help message
 	@echo  	make package          Package the project for pip installation
-	@echo  	make test             Run the tests
+	@echo  	make tests            Run the tests
 	@echo  	make format           Format the code using black
 	@echo  	make lint             Check code quality using flake8
+	@echo  	make clean            Clean not necessary files
 
-package:
-	python setup.py sdist bdist_wheel
+build:
+	python -m build
+	python -m twine check dist/*
 
-test_light:
-	python -m unittest discover $(TEST_DIR)
-
-test_medium:
-	python -m unittest discover $(TEST_DIR)
-
-test_complete:
-	python -m unittest discover $(TEST_DIR)
+upload:
+	python -m twine upload dist/*
 
+tests:
+	pytest tests/test_mandatory.py -v
 
 format:
-	black $(SRC_DIR) $(TEST_DIR)
+	black $(SRC_DIR)
 
 lint:
 	flake8 $(SRC_DIR) $(TEST_DIR)
@@ -35,9 +33,9 @@ lint:
 clean:
 	rm -rf build dist *.egg-info
 	find . -name "__pycache__" -exec rm -rf {} +
-	find . -name ".openhosta" -exec rm -rf {} +
+	find . -name "__hostacache__" -exec rm -rf {} +
 
 fclean: clean
 	rm -rf .pytest_cache .mypy_cache
 
-.PHONY: all help package test format lint clean fclean
+.PHONY: all help build test format lint clean fclean
diff --git a/doc/Docs.md b/doc/Docs.md
index f7d424f..d646f0d 100644
--- a/doc/Docs.md
+++ b/doc/Docs.md
@@ -3,7 +3,7 @@ ___
 
 Documentation for version: **1.0**
 
-Welcome to **OpenHosta** documentation :). Here you'll find all the **explanations** you need to understand the library, as well as **usage examples** and advanced **configuration** methods for the most complex tasks. You'll also find explanations of the source code for those interested in **contributing** to this project. Check the Jupyter Notebook **test files** to help you take your first steps in discovering OpenHosta.
+Welcome to **OpenHosta** documentation :). Here you'll find all the **explanations** you need to understand the library, as well as **usage examples** and advanced **configuration** methods for the most complex tasks. You'll also find explanations of the source code for those interested in **contributing** to this project. Check the [Google Colab](https://colab.research.google.com/drive/1XKrPrhLlYJD-ULTA8WHzIMqTXkb3iIpb?usp=sharing) **test files** to help you take your first steps in discovering OpenHosta.
 
 For this project, we have adopted a [Code of Conduct](CODE_OF_CONDUCT.md) to ensure a respectful and inclusive environment for all contributors. Please take a moment to read it.
 
@@ -13,7 +13,7 @@ ___
 
 #### First Step
 
-OpenHosta is a **Python library** designed to facilitate the integration of **LLMs** into the developer's environment, by adding a layer to the Python programming language without distorting it. It is based on the **PMAC** concept, reimagining the **compilation** process in “Just-In-Time” languages. All our functionalities respect the **syntax and paradigm** of this language. 
+OpenHosta is a **Python library** designed to facilitate the integration of **LLMs** into the developer's environment, by adding a layer to the Python programming language without distorting it. It is based on the [**PMAC**](PMAC.md) concept, reimagining the **compilation** process in languages. All our functionalities respect the **syntax and paradigm** of this language. 
 
 The choice of LLM is mostly up to you, depending on your configuration level, moreover the vast majority are compatible. By default, OpenAI's **GPT-4o** is chosen. This has been tested by our team during development and **provides** a satisfaying level of functionality. 
 
@@ -31,7 +31,7 @@ OpenHosta enables you to create **complex functions**, including those that were
 
 - **Python Ecosystem**
 
-OpenHosta integrates **fully** into Python syntax. Our main goal is to push programming to a **higher level**. For example, we send *docstrings*, commonly used in Python, to the **LLMs** context. We also integrate **advanced methods** such as *lambdas* and the compatibility with *Pydantic* typing. 
+OpenHosta integrates **fully** into Python syntax. Our main goal is to push programming to a **higher level**. For example, we send *docstrings*, commonly used in Python, to the **LLMs** context. We also integrate **advanced methods** such as *lambdas* and the compatibility with *Pydantic* and *typing*. 
 
 - **Open-Source**
 
@@ -39,6 +39,25 @@ We are an Open-Source project. We believe this philosophy contributes to the **s
 
 ---
 
+##### *Legal Framework*
+
+The use of AI in a production context raises important **legal** issues. It is essential to take these issues into account to ensure the compliance of your **deployment**.
+
+- **Legal Compliance**
+For any deployment, it is recommended to verify with an **AI expert** the legal compliance of your use of AI. Indeed, AI is subject to specific **regulations**, particularly in terms of **data protection** and **privacy**. In Europe, the use of AI is governed by the **General Data Protection Regulation** (GDPR) and legislated by the **IA act**. Also, the use of AI in production is subject to specific legal obligations. It is important to take these obligations into account.
+
+- **Security**
+The use of AI can also present risks in terms of **cybersecurity**. It is important to take these risks into account to ensure the security of your deployment.
+For example **injection attacks** are a major risk when deploying an application using AI. It is important to take measures to protect your application against injection attacks, such as using data validation mechanisms and content filtering.
+
+For more information, please consult the following links:
+
+- [AI Act](https://artificialintelligenceact.eu)
+- [GDPR](https://gdpr-info.eu)
+- [Prompt Injection Attack](https://www.ibm.com/topics/prompt-injection)
+
+---
+
 Let's **get started**! First here's the **table of contents** to help you navigate through the various sections of the documentation.
 
 ### Table of Content
@@ -47,6 +66,7 @@ Let's **get started**! First here's the **table of contents** to help you naviga
     - [Introduction](#introduction)
       - [First Step](#first-step)
       - [Why use OpenHosta?](#why-use-openhosta)
+        - [*Legal Framework*](#legal-framework)
     - [Table of Content](#table-of-content)
   - [Features](#features)
     - [OpenHosta Example](#openhosta-example)
@@ -54,12 +74,18 @@ Let's **get started**! First here's the **table of contents** to help you naviga
       - [Librairie Import](#librairie-import)
       - [Basic Setup](#basic-setup)
     - ["emulate" Function](#emulate-function)
-    - [Pydantic Return](#pydantic-return)
+      - [Supported types \& Pydantic](#supported-types--pydantic)
       - [Integration Details](#integration-details)
     - ["suggest" Attributs](#suggest-attributs)
       - [Usage](#usage)
       - [Output Examples](#output-examples)
-    - ["thought"](#thought)
+    - ["thought" Function](#thought-function)
+    - [Advanced configuration](#advanced-configuration)
+      - [Introduction](#introduction-1)
+      - [Inheriting from the Model Class](#inheriting-from-the-model-class)
+      - [Custom LLM Call Function](#custom-llm-call-function)
+      - [Custom Response Handling Function](#custom-response-handling-function)
+      - [Create a new instance](#create-a-new-instance)
     - [References](#references)
 
 ---
@@ -97,7 +123,8 @@ from OpenHosta import *
 
 We recommend this import method, as it gives you all the important and stable features:
   - Emulate function
-  - Thought function 
+  - Thought function
+  - Example function
   - \_\_suggest\_\_ attributes
   - Configuration tools
 
@@ -139,10 +166,11 @@ config.set_default_model(my_model)
 
 The *emulate* function is the main feature of OpenHosta. This is the function that allows you to emulate functions with AI, i.e. the instructions will be executed in an LLM and not directly in your computer. Here's how to use it.
 
-Emulate is used inside a function, after the “return”. What it does is take the function's documentation as a “prompt” to emulate it. The way in which you write the function is therefore crucial to ensure that “emulate” works properly.
+Emulate is used inside a function or a class method, after the “return”. What it does is take the function's documentation as a “prompt” to emulate it. The way in which you write the function is therefore crucial to ensure that “emulate” works properly.
 
 Here's what you need to know:
-  - **The function prototype** is one of the elements sent to LLM. Its different fields must therefore appear clearly. Give a meaningful and precise name to your function. It's also a good idea to specify the type of arguments and the type of return to reduce the uncertainty related to LLMs. 
+  - **The function prototype** is one of the elements sent to LLM. Its different fields must therefore appear clearly. Give a meaningful and precise name to your function. It's also a good idea to specify the type of arguments and the type of return to reduce the uncertainty related to LLM.
+  
 ```python
 def function(a:int, b:dict)->str:
 ```
@@ -155,7 +183,7 @@ my_model = config.Model(
     api_key="put-your-api-key-here"
 )
 
-def find_first_name(sentence:str, id:dict)->dict:
+def find_name_age(sentence:str, id:dict)->dict:
     """
     This function find in a text the name and the age of a personn.
 
@@ -172,15 +200,38 @@ def find_first_name(sentence:str, id:dict)->dict:
 
 Note that, as seen above, you can pass a previously configured model as an emulate parameter.
 
-Be careful, you can put regular instructions in your function and they will be executed. However, emulate doesn't take into account any changes to internal variables.
+Be careful, you can put regular instructions in your function and they will be executed. However, `emulate` retrieves all the variables local to the functions and gives them to the LLM as a context.
 
 emulate also accepts two other arguments: `creativity` and `diversity`. It correspond to the "temperature" and "top_p" parameters of LLMs. These values range from 0 to 1 (inclusive). For more information, please refer to the official [OpenAI documentation](https://openai.com/).
 
-### Pydantic Return 
+#### Supported types & Pydantic
+
+`emulate` support for the **typing** module: You can now use specific return types from the typing module, including [`List`, `Dict`, `Tuple`, `Set`, `FrozenSet`, `Deque`, `Iterable`, `Sequence`, `Mapping`, `Union`, `Optional`, `Literal`].
+
+```python
+from OpenHosta import emulate
+
+def analyze_text(text: str) -> Dict[str, List[Tuple[int, str]]]:
+    """Analyze text to map each word to a list of tuples containing word length and word."""
+    return emulate()
+
+# Example usage
+analysis = analyze_text("Hello, World!")
+
+print(analysis)
+print(type(analysis))
+```
+
+It also includes a verification output that checks and converts the output to the specified type if necessary. Supported types include **Pydantic** models, all types from the **typing** module mentioned above, as well as built-in types such as `dict`, `int`, `float`, `str`, `list`, `set`, `frozenset`, `tuple`, and `bool`.
+The `complex` type is not supported. If you need to use complex numbers, please pass them as a `tuple` and manually convert them after processing.
+
+For more information about Typing module, please check the official [Typing documentation](https://docs.python.org/3/library/typing.html)
 
 OpenHosta integrates with Pydantic, a library for data validation and settings management using Python type annotations. This integration allows you to use Pydantic models directly within `emulate`, ensuring data consistency and validation.
 
-Pydantic provides a way to define data models using Python types. It automatically validates and converts input data to match these types, ensuring that your application processes data safely and accurately. For more information, please read the official [Pydantic documentation](https://docs.pydantic.dev/latest/api/base_model/).
+Pydantic provides a way to define data models using Python types. It automatically validates and converts input data to match these types, ensuring that your application processes data safely and accurately.
+
+For more information, please read the official [Pydantic documentation](https://docs.pydantic.dev/latest/api/base_model/).
 
 #### Integration Details
 
@@ -217,6 +268,8 @@ def find_first_name(sentence:str)->Personn:
     return emulate()
 ```
 
+Note that the Pydantic model cannot be defined inside a function, as this will produce an error.
+
 ### "suggest" Attributs
 
 When you use the emulate function, an attribute is automatically attached. This attribute is a function giving you hints on how to improve your prompt, and a diagram visualization tool. This tool uses the default model to operate.
@@ -245,6 +298,10 @@ In this example, you can see that after calling the emulated function, we can ca
   - `advanced`: Similar to `enhanced prompt` but adds an iteration. The AI will then try to solve advanced problems according to context or other factors. Especially useful in the most complex cases.
   - `diagramm`: Gives a Mermaid diagram showing the stages of AI thinking. Useful if you want to try coding the function yourself.
 
+You can also retrieve the entire LLM response by storing the output of the `suggest` function.
+
+Note that this feature uses the default model.
+
 #### Output Examples
 
 - **Enhanced prompt:**
@@ -267,7 +324,7 @@ graph LR
     I --> J[End]
 ```
 
-### "thought"
+### "thought" Function
 
 **Lambda** functions in Python provide a way to create small, anonymous functions. These are defined using the lambda keyword and can have any number of input parameters but only a single expression.
 
@@ -286,13 +343,197 @@ Here's how it works:
 from OpenHosta import thought
 
 x = thought("Is it a masculine name")
-print(c("John"))  # True
+print(x("John"))  # True
 
-result = thought("Multiplie by two")(2)
+result = thought("Multiply by two")(2)
 print(result)   # 4
 ```
 
-Note that this feature uses the default model.
+In the example above, we can see two distinct ways of using `thought`. In the first example, you can store a lambda function in a variable and then use it. You can also call it directly by enclosing the arguments behind it in brackets. `thought` accepts multiple arguments and all types native to python. However, the content of the first bracket is always a string.
+
+The `thought` function has an initial pre-compilation stage where it predicts the type of the return value by making an initial call to an LLM. Execution time can therefore be increased.
+You can retrieve the predicted return type with the `_return_type` attribute attached to the object:
+
+```python
+from OpenHosta import thought
+
+x = thought("Adds all integers")
+ret = x(2 ,3 ,6)
+print(x._return_type) # int
+```
+
+**Note** : ***this feature uses the default model.***
+
+### "example" Function
+
+The "example" function is designed to enhance the context of a function for a LLM by adding examples to it. This functionality is encapsulated in the `example` function.
+
+**Key Characteristics**
+
+- **Versatile**: The "example" function can be used both inside and outside a function to specify examples.
+- **Save**: The "example" function provides a tool called `save_examples` that can store all the examples added to a specified function in a ***JSONL*** file.
+- **Load**: The function also offers a tool called `load_examples` to load a ***JSONL*** file into the context of the function.
+
+Here's how it works: 
+
+```python
+from OpenHosta import emulate, example
+
+def translate(text:str, language:str)->str:
+    """
+    This function translates the text in the “text” parameter into the language specified in the “language” parameter.
+    """
+    example("Bonjour Monde !", "portuguese", hosta_out="ola mundo" )
+    return emulate()
+
+
+example(text="Hello World !", language="japanese", hosta_out="こんにちは世界!", hosta_func=translate)
+
+print(translate("Hello World !", "French"))
+```
+
+The "example" function will verify the correlation between the specified input and the parameters of the function. The output should be specified only in the *hosta_out* parameter. If the example are used outside a function, please use the *hosta_func* parameter to specify a function.
+
+Now here's how works `save_examples` and `load_examples`
+
+```python
+from OpenHosta import save_examples, load_examples
+
+save_examples(hosta_func=translate, hosta_path="translate_func_example")
+
+#######
+
+def another_translate(text:str, language:str)->str:
+    """
+    This function translates the text in the “text” parameter into the language specified in the “language” parameter.
+    """
+    return emulate()
+
+load_examples(hosta_path="translate_func_example.jsonl", hosta_func=another_translate)
+# add the jsonl at the end of the path !
+```
+
+output of the `translate_func_example.jsonl`
+
+```JsonL
+{"text": "Hello World !", "language": "japanese", "hosta_out": "こんにちは世界!"}
+{"text": "Bonjour Monde !", "language": "portuguese", "hosta_out": "ola mundo"}
+```
+
+
+**Notes**: *All examples provided for a function are stored in a directory at the root of your environment. You can see it as* ***\_\_hostacache__***.
+
+
+
+### Advanced configuration
+
+
+#### Introduction
+
+This section explains how to customize the program to make its own LLM call and response handling functions. This can be useful if you need a specific functionality that is not provided by the standard library, or to enable compatibility with a specific LLM.
+
+#### Inheriting from the Model Class
+
+The first step is to inherit from the Model class to create your own configuration class. The Model class provides a base for the library's configuration, including connection settings to the LLM and response handling functions.
+
+```python
+from OpenHosta import Model
+
+class MyModel(Model):
+    # Your code here
+```
+
+In the example above, we have created a new class `MyModel` that inherits from the Model class. This means that `MyModel` has access to all the methods and attributes of the Model class. You can now add your own functions to this class to customize the OpenHosta's configuration.
+You can also override an existing function to modify its behavior. It is important to keep input/output components identical in order to avoid errors when interacting with calling functions.
+
+#### Custom LLM Call Function
+
+To create your own LLM call function, you need to override the `api_call` method of the Model class. This method is called every time the library needs to communicate with the LLM.
+
+```python
+from OpenHosta import Model
+
+class MyModel(Model):
+    def api_call(self, sys_prompt, user_prompt, creativity, diversity):
+        # Your code here
+        # Call the LLM and return the response
+        return response
+```
+
+In the example above, we have overridden the "api_call" method of the Model class to create our own LLM call function.
+The "api_call" method takes four arguments:
+
+- **sys_prompt**: a string that specifies the system prompt to be sent to the LLM. It's all the basic instructions to give context to the LLM. It is not function-dependent, but feature-dependent.. System prompts provided by OpenHosta are present in the “prompt.json” file.
+- **user_prompt**: a string that specifies the user prompt to be sent to the LLM. It contains all the information relating to the emulated function. They are placed side by side, with sentences to separate each piece of information. This section is therefore unique for each case. 
+- **creativity**: a float that specifies the creativity level of the LLM's response.
+- **diversity**: a float that specifies the diversity level of the LLM's response.
+
+The "api_call" method returns a response object that contains the LLM's response to the user prompt.
+
+#### Custom Response Handling Function
+
+To create your own response handling function, you need to override the "request_handler" method of the Model class. This method is called every time the library receives a response from the LLM.
+
+```python
+from OpenHosta import Model
+
+class MyModel(Model):
+    def request_handler(self, response, return_type, return_caller):
+        # Your code here
+        # Process the LLM response and return the result
+        return result
+```
+
+In the example above, we have overridden the `request_handler` method of the Model class to create our own response handling function. 
+The "request_handler" method takes three arguments:
+
+- **response**: the response object returned by the LLM.
+- **return_caller**: the type of the return value. For an “int” return type, it would be as follows
+  ```<class :'int'>```
+- **return_type**: a JSON describing the type of the return value. This JSON is mainly used for more complex return types such as Pydantic models or classes. For an “int” return type, the JSON would be as follows:
+  ```{'properties': {'return_hosta_type': {'title': 'Return Hosta Type', 'type': 'integer'}}, 'required': ['return_hosta_type'], 'title': 'Hosta_return_shema', 'type': 'object'}```
+
+The "request_handler" method returns the processed return value. This is the value returned by the emulated function.
+
+#### Create a new instance
+
+You can now create an instance of the class you've just created and use it in all OpenHosta's features.
+
+```python
+from OpenHosta import config, Model, emulate, thought
+
+class MyModel(Model):
+
+    def api_call(self, sys_prompt, user_prompt, creativity, diversity):
+        # Your code here
+        # Call the LLM and return the response
+        return response
+
+    def request_handler(self, response, return_type, return_caller):
+        # Your code here
+        # Process the LLM response and return the result
+        return l_ret
+
+new_model = MyModel(
+    model="model-name"
+    base_url="base-url"
+    api_key="put-your-api-key-here"
+)
+
+def capitalize(a:str)->str:
+    """
+    This function capitalize a sentence in parameter.
+    """
+    return emulate(model=new_model)
+
+print(capitalize("hello world!"))
+
+config.set_default_model(new_model)
+
+x = thought("Translate in german")
+ret = x("Hello World")
+print(ret)
+```
 
 ---
 
diff --git a/doc/PMAC.md b/doc/PMAC.md
new file mode 100644
index 0000000..969c7c5
--- /dev/null
+++ b/doc/PMAC.md
@@ -0,0 +1,32 @@
+
+# Compilateur assisté par modèle pré-entraîné (PMAC)
+
+**Un [Compilateur](https://fr.wikipedia.org/wiki/Compilateur) Assisté par Modèle Pré-Entraîné** (Acronyme anglais : PMAC) est un concept qui utilise les [modèles de langage multimodaux](https://fr.wikipedia.org/wiki/Mod%C3%A8les_de_langage_multimodaux) de grande taille (MLLM) pour améliorer le processus de compilation. Ce concept vise à rapprocher le [langage informatique](https://fr.wikipedia.org/wiki/Langage_informatique) compilable du [langage naturel](https://fr.wikipedia.org/wiki/Langage_naturel) tout en respectant la syntaxe et la structure établies des langages de programmation.
+
+## Histoire
+
+L'apparition des [transformeurs](https://fr.wikipedia.org/wiki/Transformeur), introduits par [Google](https://fr.wikipedia.org/wiki/Google) en 2017, a radicalement changé le paysage technologique, en particulier dans le domaine du [traitement du langage naturel](https://fr.wikipedia.org/wiki/Traitement_du_langage_naturel). Ces modèles ont ouvert la voie à des avancées significatives, perturbant les méthodes traditionnelles d'interaction linguistique. En 2022, [OpenAI](https://fr.wikipedia.org/wiki/OpenAI) a lancé [ChatGPT](https://fr.wikipedia.org/wiki/ChatGPT), qui a marqué un tournant majeur en établissant de nouvelles références en matière de dialogue automatisé et d'interaction avec des machines.
+
+Parmi les innovations qui ont suivi, [LangChain](https://www.langchain.com/), introduit en 2023, a permis aux développeurs d'intégrer des modèles de langage avancés dans les applications logicielles. Cette évolution a simplifié le développement d'applications conversationnelles et ouvert la voie à l'utilisation généralisée de l'IA dans des contextes de programmation traditionnels.
+
+En parallèle, 2023 a vu [GitHub](https://fr.wikipedia.org/wiki/GitHub) introduire [Copilot](https://fr.wikipedia.org/wiki/GitHub_Copilot), une assistance IA permettant de suggérer du code contextuel, révolutionnant la productivité et le processus de codage.
+
+Dans ce contexte d'innovation rapide, l'intégration de l'intelligence artificielle dans une multitude de domaines, y compris le processus de compilation, devient évidente. En 2024, le concept de PMAC voit le jour à Lyon, conçu par un équipe de passionées : [Emmanuel Batt](https://fr.wikipedia.org/wiki/Emmanuel_Batt), [Léandre Ramos](https://fr.wikipedia.org/wiki/L%C3%A9andre_Ramos), [William Jolivet](https://fr.wikipedia.org/wiki/William_Jolivet) et [Merlin Devillard](https://fr.wikipedia.org/wiki/Merlin_Devillard). En parallèle, [Meta](https://fr.wikipedia.org/wiki/Meta_(entreprise)) explore une approche similaire avec le développement de son [LLM Compiler](https://arxiv.org/abs/2407.02524).
+
+## Principe de Base
+
+Avec l'émergence de l'intelligence artificielle et l'évolution continue des modèles de langage, en particulier les modèles multimodaux, les capacités de traitement du NLP ([Natural Language Processing](https://fr.wikipedia.org/wiki/Traitement_automatique_des_langues)) se sont considérablement améliorées. Cette avancée permet de réévaluer les étapes traditionnelles de la compilation en intégrant une compréhension plus profonde et contextuelle des langages de programmation.
+
+En programmation, un compilateur est un outil qui traduit un [langage de programmation](https://fr.wikipedia.org/wiki/Langage_de_programmation) en un [langage machine](https://fr.wikipedia.org/wiki/Langage_machine) , permettant aux ordinateurs d'exécuter les instructions. Les langages de programmation varient par leur niveau d'abstraction, allant des [langages de bas niveau](https://fr.wikipedia.org/wiki/Langage_de_programmation_de_bas_niveau), proches de l'architecture matérielle (langage machine), aux [langages de haut niveau](https://fr.wikipedia.org/wiki/Langage_de_programmation_de_haut_niveau), conçus pour être plus abstraits et lisibles pour les humains.
+
+Le concept de PMAC utilise les MLLM pour traduire du code contenant des expressions plus claires et concrètes. Cette approche confie le traitement des ambiguïtés au PMAC, ce qui permet de gérer des aspects que les compilateurs traditionnels ne pouvaient pas traiter auparavant. Cela offre une interprétation plus précise des opérations programmatiques, permettant ainsi l'apparition de fonctions plus nuancées et complexes.
+
+## Avantages et Inconvénients
+
+|**Avantages**|**Inconvénients**|
+|---|---|
+|**Flexibilité des entrées :** Les fonctions peuvent être spécifiées en langage naturel, permettant ainsi l'exécution d'actions complexes qui seraient autrement difficiles, voire impossibles, à réaliser avec des instructions machine traditionnelles.|**Temps de compilation plus lent :** L'utilisation de MLLM peut entraîner des temps de compilation plus longs par rapport aux méthodes traditionnelles.|
+|**Variété des stratégies d'implémentation :** Le compilateur PMAC peut générer divers types de stratégies d'implémentation, facilitant ainsi l'intégration de différents modes d'exécution adaptés aux besoins spécifiques de l'utilisateur.|**Coût plus élevé :** Les appels [API](https://fr.wikipedia.org/wiki/Interface_de_programmation) aux modèles ou leur stockage peut entraîner des coûts supplémentaires significatifs.|
+|**Optimisation du code :** Grâce à la vaste connaissance des [LLM](https://fr.wikipedia.org/wiki/Grand_mod%C3%A8le_de_langage) en matière de code, PMAC intègre efficacement des techniques d'optimisation, améliorant ainsi les performances du code compilé par une compréhension approfondie des structures et logiques sous-jacentes.|**Imperfections des modèles :** Les modèles peuvent présenter des erreurs ou des divergences, posant des risques potentiels pour la fiabilité du code.|
+
+ 
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
index 121b9b3..c4e9125 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
 
 [project]
 name = "OpenHosta"
-version = "1.0.2"
+version = "1.1.0-rc1"
 description = "Open-Source programming project IA integretion in developement environnement"
 keywords = ["AI", "GPT", "Natural language", "Autommatic", "Easy"]
 authors = [
@@ -28,7 +28,9 @@ classifiers = [
 dependencies = [
   "requests>=2.32.3",
   "pydantic>=2.8.2",
-  "tiktoken>=0.7.0"
+  "tiktoken>=0.7.0",
+  "jsonschema>=4.23.0",
+  "typing-extensions>=4.12.2"
 ]
 
 [project.urls]
@@ -42,4 +44,11 @@ package-dir = { "" = "src" }
 where = ["src"]
 
 [tool.setuptools.package-data]
-"OpenHosta" = ["*.json"]
\ No newline at end of file
+"OpenHosta" = ["*.json"]
+
+[tool.pytest.ini_options]
+testpaths = [
+    'tests/functionnalTests',
+    'tests/unitTests',
+    'tests/performanceTests'
+]
\ No newline at end of file
diff --git a/requirements-dev.txt b/requirements-dev.txt
new file mode 100644
index 0000000..3566307
--- /dev/null
+++ b/requirements-dev.txt
@@ -0,0 +1,1 @@
+pytest==8.3.2
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index e8b3378..72dbf5d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,5 @@
 requests==2.32.3
 pydantic==2.8.2
-tiktoken==0.7.0
\ No newline at end of file
+tiktoken==0.7.0
+jsonschema==4.23.0
+typing-extensions==4.12.2
\ No newline at end of file
diff --git a/src/OpenHosta.egg-info/PKG-INFO b/src/OpenHosta.egg-info/PKG-INFO
index 1f6b36e..33b5f92 100644
--- a/src/OpenHosta.egg-info/PKG-INFO
+++ b/src/OpenHosta.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: OpenHosta
-Version: 1.0.2
+Version: 1.1.0rc1
 Summary: Open-Source programming project IA integretion in developement environnement
 Author: Léandre Ramos, Merlin Devillard, William Jolivet, Emmanuel Batt
 License: MIT License
@@ -41,6 +41,8 @@ License-File: LICENSE
 Requires-Dist: requests>=2.32.3
 Requires-Dist: pydantic>=2.8.2
 Requires-Dist: tiktoken>=0.7.0
+Requires-Dist: jsonschema>=4.23.0
+Requires-Dist: typing-extensions>=4.12.2
 
 # OpenHosta 
 v1.0 - Open-Source Project
@@ -139,20 +141,18 @@ This way you have all the documentation and source code to understand our projec
 ### Example
 
 ```python
-from openhosta import *
+from OpenHosta import emulate, config
 
-config.set_default_apiKey("example-apikey")
+config.set_default_apiKey("put-your-api-key-here")
 
-def my_func(a:int, b:str)->dict:
-   """
-   This Function does something.
-   """
-   return emulate()
+def translate(text:str, language:str)->str:
+    """
+    This function translates the text in the “text” parameter into the language specified in the “language” parameter.
+    """
+    return emulate()
 
-my_func(5, "Hello World!")
-
-my_lambda = thought("Do something")
-my_lambda(5)
+result = translate("Hello World!", "French")
+print(result)
 ```
 You check OpenHosta's [documentation](doc/Docs.md) for more detailled informations or exemple
 
diff --git a/src/OpenHosta.egg-info/SOURCES.txt b/src/OpenHosta.egg-info/SOURCES.txt
index 7057834..68f278f 100644
--- a/src/OpenHosta.egg-info/SOURCES.txt
+++ b/src/OpenHosta.egg-info/SOURCES.txt
@@ -1,16 +1,19 @@
 LICENSE
 README.md
 pyproject.toml
+src/OpenHosta/OpenHosta.py
 src/OpenHosta/__init__.py
 src/OpenHosta/analytics.py
+src/OpenHosta/cache.py
 src/OpenHosta/config.py
 src/OpenHosta/emulate.py
 src/OpenHosta/enhancer.py
 src/OpenHosta/errors.py
+src/OpenHosta/example.py
 src/OpenHosta/exec.py
-src/OpenHosta/openhosta.py
 src/OpenHosta/prompt.json
 src/OpenHosta/prompt.py
+src/OpenHosta/thought.py
 src/OpenHosta.egg-info/PKG-INFO
 src/OpenHosta.egg-info/SOURCES.txt
 src/OpenHosta.egg-info/dependency_links.txt
diff --git a/src/OpenHosta/OpenHosta.py b/src/OpenHosta/OpenHosta.py
index 5a2358d..0168b54 100644
--- a/src/OpenHosta/OpenHosta.py
+++ b/src/OpenHosta/OpenHosta.py
@@ -1,9 +1,26 @@
-from .emulate import _exec_emulate, thought
+from .config import Model, DefaultManager
+
+DefaultManager.set_default_model(
+    Model(model="gpt-4o", base_url="https://api.openai.com/v1/chat/completions")
+)
+
+
+from .emulate import _exec_emulate
 from . import config
-from .config import Model
+from .thought import thought
 from .exec import HostaInjector
-
+from .example import example, load_examples, save_examples
 
 emulate = HostaInjector(_exec_emulate)
 
-__all__ = "emulate", "thought", "config", "Model"
\ No newline at end of file
+__all__ = (
+    "emulate",
+    "thought",
+    "example", 
+    "load_examples", 
+    "save_examples",
+    "config", 
+    "Model", 
+    "DefaultManager"
+)
+
diff --git a/src/OpenHosta/__init__.py b/src/OpenHosta/__init__.py
index 8c4b1a7..058813c 100644
--- a/src/OpenHosta/__init__.py
+++ b/src/OpenHosta/__init__.py
@@ -1,1 +1,1 @@
-from .openhosta import *
\ No newline at end of file
+from .OpenHosta import *
diff --git a/src/OpenHosta/analytics.py b/src/OpenHosta/analytics.py
index 5df2973..9b59686 100644
--- a/src/OpenHosta/analytics.py
+++ b/src/OpenHosta/analytics.py
@@ -2,16 +2,17 @@
 from enum import Enum
 import time as t
 import sys
-import requests
 import json
 
-from .config import Model, _default_model
+from .config import Model, DefaultManager
 from .prompt import PromptMananger
 
 _x = PromptMananger()
 
 _estimate_prompt = _x.get_prompt("estimate")
 
+l_default = DefaultManager.get_default_model()
+
 
 class ModelAnalizer(Model):
 
@@ -52,7 +53,7 @@ def get_token_perSec(self):
         return self.token_perSec
 
     def _estimate_output_token(self, function_doc: str, function_call: str):
-        global _estimate_prompt, _default_model
+        global _estimate_prompt, l_default
 
         try:
             if not _estimate_prompt:
@@ -61,38 +62,19 @@ def _estimate_output_token(self, function_doc: str, function_call: str):
             sys.stderr.write(f"[ESTIMATE_ERROR]: {v}")
             return None
 
-        api_key = _default_model.api_key
-        l_body = {
-            "model": _default_model.model,
-            "messages": [
-                {
-                    "role": "system",
-                    "content": [
-                        {
-                            "type": "text",
-                            "text": _estimate_prompt
-                            + "---\n"
-                            + str(function_doc)
-                            + "\n---",
-                        }
-                    ],
-                },
-                {
-                    "role": "user",
-                    "content": [{"type": "text", "text": str(function_call)}],
-                },
-            ],
-            "response_format": {"type": "json_object"},
-            "temperature": 0.1,
-            "top_p": 0.1,
-        }
-
-        headers = {
-            "Content-Type": "application/json",
-            "Authorization": f"Bearer {api_key}",
-        }
-
-        response = requests.post(_default_model.base_url, json=l_body, headers=headers)
+        l_user_prompt = (
+            "\n Here's the fonction documentation:\n"
+            + f"{function_doc}\n"
+            + "Here's the function call:\n"
+            + f"{function_call}\n"
+        )
+
+        response = l_default._api_call(
+            sys_prompt=_estimate_prompt,
+            user_prompt=l_user_prompt,
+            creativity=0.2,
+            diversity=0.2,
+        )
 
         if response.status_code == 200:
             data = response.json()
diff --git a/src/OpenHosta/cache.py b/src/OpenHosta/cache.py
new file mode 100644
index 0000000..a0de4a6
--- /dev/null
+++ b/src/OpenHosta/cache.py
@@ -0,0 +1,165 @@
+import pickle
+import os
+import hashlib
+import inspect
+from typing import Callable, Dict, Any, get_origin, get_args
+import typing
+import collections
+from pydantic import BaseModel, create_model
+
+
+CACHE_DIR = "__hostacache__"
+os.makedirs(CACHE_DIR, exist_ok=True)
+
+
+class Hostacache:
+    def __init__(self, func, cache_id, value) -> None:
+        self.func = func
+        self.cache_id = cache_id
+        self.value = value
+        self.infos_cache = {
+            "hash_function": "",
+            "function_def": "",
+            "return_type": "",
+            "return_caller": "",
+            "function_call": "",
+            "function_locals": {},
+            "ho_example": [],
+            "ho_example_id": 0,
+            "ho_cothougt": [],
+            "ho_cothougt_id": 0,
+        }
+
+    def __call__(self):
+        func_name = self.func.__name__
+        path_name = os.path.join(CACHE_DIR, f"{func_name}.openhc")
+
+        if os.path.exists(path_name):
+            with open(path_name, "rb") as f:
+                cached_data = pickle.load(f)
+            assert self.cache_id in cached_data, "Cache ID not found in cache file"
+
+            if self._is_value_already_in_example(self.value, cached_data) == False:
+                cached_data[str(self.cache_id)].append(self.value)
+                cached_data[f"{str(self.cache_id)}" + "_id"] = self._get_hashFunction(
+                    str(cached_data[str(self.cache_id)]), 0, 0
+                )
+                cached_data["hash_function"] = self._get_hashFunction(
+                    cached_data["function_def"],
+                    cached_data["ho_example_id"],
+                    cached_data["ho_cothougt_id"],
+                )
+                with open(path_name, "wb") as f:
+                    pickle.dump(cached_data, f)
+            return
+        hosta_args = self._get_argsFunction(self.func)
+        with open(path_name, "wb") as f:
+            pickle.dump(hosta_args, f)
+        return
+
+    def _is_value_already_in_example(self, value, cached_data):
+        for item in cached_data["ho_example"]:
+            if isinstance(item, dict):
+                if item == value:
+                    return True
+            elif isinstance(item, list):
+                for sub_item in item:
+                    if sub_item == value:
+                        return True
+        return False
+
+    def _get_hashFunction(self, func_def: str, nb_example: int, nb_thought: int) -> str:
+        combined = f"{func_def}{nb_example}{nb_thought}"
+        return hashlib.md5(combined.encode()).hexdigest()
+
+    def _get_argsFunction(self, func_obj):
+        self.infos_cache["function_def"], func_prot = self._get_functionDef(func_obj)
+        self.infos_cache["return_type"], self.infos_cache["return_caller"] = (
+            self._get_functionReturnType(func_obj)
+        )
+        self.infos_cache[self.cache_id].append(self.value)
+        self.infos_cache[f"{str(self.cache_id)}" + "_id"] = self._get_hashFunction(
+            str(self.infos_cache[str(self.cache_id)]), 0, 0
+        )
+        self.infos_cache["hash_function"] = self._get_hashFunction(
+            self.infos_cache["function_def"],
+            self.infos_cache["ho_example_id"],
+            self.infos_cache["ho_cothougt_id"],
+        )
+        return self.infos_cache
+
+    def _get_functionDef(self, func: Callable) -> str:
+        sig = inspect.signature(func)
+
+        func_name = func.__name__
+        func_params = ", ".join(
+            [
+                (
+                    f"{param_name}: {param.annotation.__name__}"
+                    if param.annotation != inspect.Parameter.empty
+                    else param_name
+                )
+                for param_name, param in sig.parameters.items()
+            ]
+        )
+        func_return = (
+            f" -> {sig.return_annotation.__name__}"
+            if sig.return_annotation != inspect.Signature.empty
+            else ""
+        )
+        definition = f"def {func_name}({func_params}):{func_return}\n    '''\n    {func.__doc__}\n    '''"
+        prototype = f"def {func_name}({func_params}):{func_return}"
+        return definition, prototype
+
+    def _inspect_returnType(self, func: Callable) -> str:
+        sig = inspect.signature(func)
+
+        if sig.return_annotation != inspect.Signature.empty:
+            return sig.return_annotation
+        else:
+            return None
+
+    def _get_typingOrigin(self, return_type) -> bool:
+        origin = get_origin(return_type)
+        return origin in {
+            list,
+            dict,
+            tuple,
+            set,
+            frozenset,
+            typing.Union,
+            typing.Optional,
+            typing.Literal,
+            collections.deque,
+            collections.abc.Iterable,
+            collections.abc.Sequence,
+            collections.abc.Mapping,
+        }
+
+    def _get_functionReturnType(self, func: Callable) -> Dict[str, Any]:
+        return_caller = self._inspect_returnType(func)
+        return_type = None
+
+        if return_caller is not None:
+            if self._get_typingOrigin(return_caller):
+                return_caller_origin = get_origin(return_caller)
+                return_caller_args = get_args(return_caller)
+                combined = return_caller_origin[return_caller_args]
+                new_model = create_model(
+                    "Hosta_return_shema", return_hosta_type_typing=(combined, ...)
+                )
+                return_type = new_model.model_json_schema()
+            elif issubclass(return_caller, BaseModel):
+                return_type = return_caller.model_json_schema()
+            else:
+                new_model = create_model(
+                    "Hosta_return_shema", return_hosta_type=(return_caller, ...)
+                )
+                return_type = new_model.model_json_schema()
+        else:
+            No_return_specified = create_model(
+                "Hosta_return_shema", return_hosta_type_any=(Any, ...)
+            )
+            return_type = No_return_specified.model_json_schema()
+
+        return return_type, return_caller
diff --git a/src/OpenHosta/config.py b/src/OpenHosta/config.py
index 5c71b80..7861675 100644
--- a/src/OpenHosta/config.py
+++ b/src/OpenHosta/config.py
@@ -2,6 +2,9 @@
 import requests
 import json
 import re
+from jsonschema import validate
+from pydantic import BaseModel
+from typing import get_origin, get_args
 
 
 def is_valid_url(url):
@@ -26,12 +29,22 @@ def __init__(self, model: str = None, base_url: str = None, api_key: str = None)
         self.model = model
         self.base_url = base_url
         self.api_key = api_key
+        self.conversion_function = {
+            str: lambda x: str(x),
+            int: lambda x: int(x),
+            float: lambda x: float(x),
+            list: lambda x: list(x),
+            set: lambda x: set(x),
+            frozenset: lambda x: frozenset(x),
+            tuple: lambda x: tuple(x),
+            bool: lambda x: bool(x),
+        }
 
         if any(var is None for var in (model, base_url)):
             sys.stderr.write(f"[CONFIG_ERROR] Empty values.")
             return
         elif not is_valid_url(self.base_url):
-            sys.stderr.write(f"[CONFIG_ERROR Invalid URL.")
+            sys.stderr.write(f"[CONFIG_ERROR] Invalid URL.")
             return
 
     def __str__(self) -> str:
@@ -42,12 +55,11 @@ def __str__(self) -> str:
             "Infos:\n"
         )
 
-    def _api_call(
+    def api_call(
         self, sys_prompt: str, user_prompt: str, creativity: float, diversity: float
     ):
         if self.api_key is None or not self.api_key:
-            sys.stderr.write(f"[CALL_ERROR] Unknown API key.")
-            return
+            raise AttributeError("Empty API key.")
 
         l_body = {
             "model": self.model,
@@ -72,51 +84,105 @@ def _api_call(
 
         try:
             response = requests.post(self.base_url, json=l_body, headers=headers)
-        except Exception as e:
-            pass
+            response.raise_for_status()
+        except requests.exceptions.RequestException as e:
+            sys.stderr.write(f"[CALL_ERROR] Request failed: {e}\n")
         if response.status_code != 200:
-            sys.stderr.write(f"[CALL_ERROR] API call the request was unsuccessful.")
+            sys.stderr.write(
+                f"[CALL_ERROR] API call the request was unsuccessful. Status code: {response.status_code}:\n{response.text}"
+            )
         return response
 
-    def _request_handler(self, response):
+    def request_handler(self, response, return_type, return_caller):
         l_ret = ""
 
         data = response.json()
         json_string = data["choices"][0]["message"]["content"]
-        
-        
+
         try:
             l_ret_data = json.loads(json_string)
+            validate(
+                instance=l_ret_data.get("return", {}),
+                schema=return_type.get("properties", {}),
+            )  # Here
 
         except json.JSONDecodeError as e:
             sys.stderr.write(f"JSONDecodeError: {e}")
             l_cleand = "\n".join(json_string.split("\n")[1:-1])
             l_ret_data = json.loads(l_cleand)
+            l_ret = l_ret_data["return"]
+            return l_ret
+
+        if "return_hosta_type" in return_type["properties"]:
+            if return_caller in self.conversion_function:
+                convert_function = self.conversion_function[return_caller]
+                l_ret = convert_function(l_ret_data["return"])
+            else:
+                l_ret = l_ret_data["return"]
+
+        elif "return_hosta_type_typing" in return_type["properties"]:
+            l_ret = self.convert_to_type(l_ret_data["return"], return_caller)
 
-        l_ret = l_ret_data["return"]
+        elif "return_hosta_type_any" in return_type["properties"]:
+            l_ret = l_ret_data["return"]
+
+        elif issubclass(return_caller, BaseModel):
+            l_ret = return_caller(**l_ret_data["return"])
+
+        else:
+            l_ret = l_ret_data["return"]
 
         return l_ret
 
+    def convert_to_type(self, data, type):
+        origin = get_origin(type)
+        args = get_args(type)
 
-_default_model = Model(
-    model="gpt-4o",
-    base_url="https://api.openai.com/v1/chat/completions",
-)
+        if origin != None:
+            if origin in self.conversion_function:
+                convert_function = self.conversion_function[origin]
+                return convert_function(self.convert_to_type(d, args[0]) for d in data)
 
+        return data
 
-def set_default_model(new: Model):
-    global _default_model
 
-    _default_model = new
+class DefaultModel:
+    _instance = None
 
+    def __new__(cls, *args, **kwargs):
+        if cls._instance is None:
+            cls._instance = super(DefaultModel, cls).__new__(cls, *args, **kwargs)
+        return cls._instance
 
-def set_default_apiKey(api_key=None):
-    global _default_model
+    def __init__(self):
+        if not hasattr(self, "model"):
+            self.model = None
+
+    def set_default_model(self, new):
+        if isinstance(new, Model):
+            self.model = new
+        else:
+            sys.stderr.write("[CONFIG_ERROR] Invalid model instance.\n")
 
-    if api_key is not None or isinstance(api_key, str):
-        _default_model.api_key = api_key
-    else:
-        sys.stderr.write("[CONFIG_ERROR] Invalid API key.")
+    def set_default_apiKey(self, api_key=None):
+        if api_key is not None or isinstance(api_key, str):
+            self.model.api_key = api_key
+        else:
+            sys.stderr.write("[CONFIG_ERROR] Invalid API key.")
+
+    def get_default_model(self):
+        return self.model
+
+
+DefaultManager = DefaultModel()
+
+
+def set_default_model(new):
+    DefaultManager.set_default_model(new)
+
+
+def set_default_apiKey(api_key=None):
+    DefaultManager.set_default_apiKey(api_key)
 
 
-__all__ = Model, _default_model, set_default_model, set_default_apiKey
+__all__ = Model, set_default_apiKey, set_default_model, DefaultManager
diff --git a/src/OpenHosta/emulate.py b/src/OpenHosta/emulate.py
index 1370e5f..edb7714 100644
--- a/src/OpenHosta/emulate.py
+++ b/src/OpenHosta/emulate.py
@@ -1,25 +1,69 @@
 import sys
 
-from .analytics import request_timer
 from .prompt import PromptMananger
-from .config import _default_model, Model, set_default_apiKey
+from .config import Model, DefaultManager
+
 
 _x = PromptMananger()
 
 _emulator_pre_prompt = _x.get_prompt("emulate")
 
+l_default = DefaultManager.get_default_model()
+
+
+def build_user_prompt(_function_infos: dict = None):
+    function_doc = _function_infos["function_def"]
+    function_call = _function_infos["function_call"]
+    function_return = _function_infos["return_type"]
+    function_example = _function_infos["ho_example"]
+    function_locals = _function_infos["function_locals"]
+
+    user_prompt = (
+        "Here's the function definition:\n"
+        + function_doc
+        + "\nAnd this is the function call:\n"
+        + function_call
+    )
+
+    if function_example != []:
+        user_prompt = (
+            user_prompt
+            + "\nHere are some examples of expected input and output:\n"
+            + str(function_example)
+        )
+
+    if not function_return is None:
+        user_prompt = (
+            user_prompt
+            + '\nTo fill the “return” value in the output JSON, build your response as defined in the following JSON schema. Do not change the key "return"\n'
+            + str(function_return)
+        )
+
+    if function_locals != {}:
+        user_prompt = (
+            user_prompt
+            + "\nHere's the function's locals variables which you can use as additional information to give your answer:\n"
+            + str(function_locals)
+        )
+
+    return user_prompt
+
 
 def _exec_emulate(
-    _function_doc=None,
-    _function_call=None,
-    _function_return=None,
-    model: Model = _default_model,
-    warn: bool = False,
+    _function_infos: dict = None,
+    _function_obj: object = None,
+    model: Model = None,
     l_creativity: float = None,
     l_diversity: float = None,
 ):
     global _emulator_pre_prompt
 
+    _function_return_caller = _function_infos["return_caller"]
+    _function_return = _function_infos["return_type"]
+
+    if model is None:
+        model = DefaultManager.get_default_model()
+
     try:
         if not isinstance(_emulator_pre_prompt, str) or not _emulator_pre_prompt:
             raise ValueError("Invalid prompt.")
@@ -31,45 +75,25 @@ def _exec_emulate(
         sys.stderr.write(f"[EMULATE_ERROR]: {v}")
         return None
 
-    l_user_prompt = (
-        "Here's the function definition:\n"
-        + _function_doc
-        + "\nAnd this is the function call:\n"
-        + _function_call
-    )
-    
-    if not _function_return is None:
-        l_user_prompt = (
-            l_user_prompt 
-            + "\nTo fill the “return” value in the output JSON, build your response as defined in the following JSON schema. Do not change the key \"return\"\n"
-            + str(_function_return)
-        )
+    l_user_prompt = build_user_prompt(_function_infos)
 
-    response = model._api_call(
+    response = model.api_call(
         sys_prompt=_emulator_pre_prompt,
         user_prompt=l_user_prompt,
         creativity=l_creativity,
         diversity=l_diversity,
     )
 
-    l_ret = ""
+    if _function_obj is not None and "bound method" not in str(_function_obj):
+        setattr(_function_obj, "_last_response", response.json())
 
     if response.status_code == 200:
-        l_ret = model._request_handler(response)
+        l_ret = model.request_handler(
+            response, _function_return, _function_return_caller
+        )
+
     else:
         sys.stderr.write(f"Error {response.status_code}: {response.text}")
         l_ret = None
-    return l_ret
 
-
-def thought(key):
-    def inner_func(*args, **kwargs):
-        try:
-            result = _exec_emulate(_function_doc=key, _function_call=str(args[0]))
-        except Exception as e:
-            sys.stderr.write(Exception)
-            sys.stderr.write("[LMDA_ERROR]")
-            result = None
-        return result
-
-    return inner_func
+    return l_ret
diff --git a/src/OpenHosta/enhancer.py b/src/OpenHosta/enhancer.py
index 83f97bd..cf58c31 100644
--- a/src/OpenHosta/enhancer.py
+++ b/src/OpenHosta/enhancer.py
@@ -1,97 +1,63 @@
-import requests
 import json
 import sys
 
 from .prompt import PromptMananger
-from .config import _default_model
+from .config import DefaultManager
+
 
 _x = PromptMananger()
 _enhancer_pre_prompt = _x.get_prompt("enhance")
 
+l_default = DefaultManager.get_default_model()
+
 
 def _ai_call_enh(sys_prompt: str, func_prot: str, func_doc: str):
-    global _default_model
+    global l_default
 
-    api_key = _default_model.api_key
-    url = _default_model.base_url
+    l_user_prompt = (
+        "\nHere's my python function's prototype:\n---\n"
+        + func_prot
+        + "\n---\n"
+        + "\nHere's my python function's prompt:\n---\n"
+        + func_doc
+        + "\n---\n"
+    )
 
-    headers = {
-        "Content-Type": "application/json",
-        "Authorization": f"Bearer {api_key}",
-    }
+    response = l_default.api_call(
+        sys_prompt=sys_prompt, user_prompt=l_user_prompt, creativity=0.8, diversity=0.8
+    )
 
-    data = {
-        "model": _default_model.model,
-        "messages": [
-            {"role": "system", "content": [{"type": "text", "text": sys_prompt}]},
-            {
-                "role": "user",
-                "content": [
-                    {
-                        "type": "text",
-                        "text": "\nHere's my python function's prototype:\n---\n"
-                        + func_prot
-                        + "\n---\n",
-                    }
-                ],
-            },
-            {
-                "role": "user",
-                "content": [
-                    {
-                        "type": "text",
-                        "text": "\nHere's my python function's prompt:\n---\n"
-                        + func_doc
-                        + "\n---\n",
-                    }
-                ],
-            },
-        ],
-        "temperature": 0.7,
-        "top_p": 0.7,
-    }
+    response_data = response.json()
+    return response_data["choices"][0]["message"]["content"]
 
-    response = requests.post(url, headers=headers, data=json.dumps(data))
 
-    if response.status_code == 200:
-        response_data = response.json()
-        return response_data["choices"][0]["message"]["content"]
-    else:
-        sys.stderr.write(
-            "[CALL_ERROR] The request was unsuccessful or one of the parameters is invalid"
-        )
-        sys.stderr.write(f"Status: {response.status_code}")
-        return None
+def _parse_data(response: str, last_enh: dict) -> dict:
+    try:
+        l_ret_data = json.loads(response)
 
+    except json.JSONDecodeError as e:
+        sys.stderr.write(f"JSONDecodeError: {e}")
+        l_cleand = "\n".join(response.split("\n")[1:-1])
+        l_ret_data = json.loads(l_cleand)
 
-def _parse_data(response: str, last_enh: dict) -> dict:
-    current_section = None
-    current_text = []
-
-    for line in response.splitlines():
-        if line.startswith("->"):
-            if current_section:
-                last_enh[current_section] = "\n".join(current_text).strip()
-            current_section = line[3:].strip(":")
-            current_text = []
-        else:
-            current_text.append(line)
-    if current_section:
-        last_enh[current_section] = "\n".join(current_text).strip()
+    last_enh["enhanced"] = l_ret_data["enhanced"]
+    last_enh["review"] = l_ret_data["review"]
+    last_enh["advanced"] = l_ret_data["advanced"]
+    last_enh["mermaid"] = l_ret_data["mermaid"]
     return last_enh
 
 
 def _build_attributes(func: object, last_enh) -> int:
     try:
-        if not func.__name__ and not type(func.__name__) is str:
+        if not func.__name__ or not type(func.__name__) is str:
             raise ValueError("ValueError -> function name")
-        if not last_enh["enhanced"] and not type(last_enh["enhanced"]) is str:
+        if not last_enh["enhanced"] or not type(last_enh["enhanced"]) is str:
             raise ValueError("ValueError -> enhanced output")
-        if not last_enh["review"] and not type(last_enh["review"]) is str:
+        if not last_enh["review"] or not type(last_enh["review"]) is str:
             raise ValueError("ValueError -> review output")
-        if not last_enh["advanced"] and not type(last_enh["advanced"]) is str:
+        if not last_enh["advanced"] or not type(last_enh["advanced"]) is str:
             raise ValueError("ValueError -> seggested output")
-        if not last_enh["mermaid"] and not type(last_enh["mermaid"]) is str:
+        if not last_enh["mermaid"] or not type(last_enh["mermaid"]) is str:
             raise ValueError("ValueError -> mermaid output")
     except ValueError as e:
         sys.stderr.write(f"[BUILD_ERROR] {e}")
@@ -121,3 +87,4 @@ def enhance(func):
     last_enh = _parse_data(last_return, last_enh)
 
     _build_attributes(func, last_enh)
+    return last_enh
diff --git a/src/OpenHosta/errors.py b/src/OpenHosta/errors.py
index 2515fc2..5636317 100644
--- a/src/OpenHosta/errors.py
+++ b/src/OpenHosta/errors.py
@@ -1,8 +1,10 @@
 class OhCustomError(Exception):
     """Base class for other customs exceptions"""
+
     pass
-    
+
+
 class RequestError(OhCustomError):
     """Raised when a request to a llm went wrong"""
+
     pass
-    
\ No newline at end of file
diff --git a/src/OpenHosta/example.py b/src/OpenHosta/example.py
new file mode 100644
index 0000000..6c13b87
--- /dev/null
+++ b/src/OpenHosta/example.py
@@ -0,0 +1,152 @@
+import inspect
+import pickle
+import os
+import json
+
+from .cache import Hostacache
+
+CACHE_DIR = "__hostacache__"
+os.makedirs(CACHE_DIR, exist_ok=True)
+
+
+def example(*args, hosta_func=None, hosta_out=None, **kwargs):
+    input_type = {}
+    output_type = {}
+    example_dict = {}
+
+    if hosta_func is None:
+        try:
+            func_frame = inspect.currentframe().f_back
+            func_name = func_frame.f_code.co_name
+            func = func_frame.f_globals[func_name]
+        except:
+            raise ValueError(f"Please provide hosta_func for specifying the function")
+
+    elif callable(hosta_func):
+        func = hosta_func
+    else:
+        raise ValueError(f"Please provide hosta_func for specifying the function")
+
+    try:
+        sig = inspect.signature(func)
+        for param in sig.parameters.values():
+            input_type[param.name] = param.annotation
+        output_type["hosta_out"] = sig.return_annotation
+    except:
+        raise ValueError(f"Function does not have signature")
+    if args != ():
+        if len(args) != len(input_type):
+            raise ValueError(
+                f"Too many arguments for function {func.__name__}, please provide {len(input_type)} arguments, use hosta_out for output"
+            )
+
+        for i, arg in enumerate(args):
+            param_name = list(input_type.keys())[i]
+
+            expected_type = input_type[param_name]
+            if not isinstance(arg, expected_type):
+                raise ValueError(
+                    f"Argument {arg} does NOT match the expected type {expected_type} for parameter {param_name}. For function {func.__name__}"
+                )
+            example_dict[param_name] = arg
+
+    else:
+        if len(kwargs) != len(input_type):
+            raise ValueError(
+                f"Too many arguments for function {func.__name__}, please provide {len(input_type)} arguments, use hosta_out for output"
+            )
+
+        for key, value in kwargs.items():
+            expected_type = input_type[key]
+            if not isinstance(value, expected_type):
+                raise ValueError(
+                    f"Argument {value} does NOT match the expected type {expected_type} for parameter {key}. For function {func.__name__}"
+                )
+            example_dict[key] = value
+
+    if hosta_out is None:
+        raise ValueError(f"Please provide hosta_out for output")
+    else:
+        expected_type = output_type["hosta_out"]
+        if not isinstance(hosta_out, expected_type):
+            raise ValueError(
+                f"Output {hosta_out} does NOT match the expected type {expected_type}. For function {func.__name__}"
+            )
+        example_dict["hosta_out"] = hosta_out
+
+    cache_id = "ho_example"
+    cache = Hostacache(func, cache_id, example_dict)
+    cache()
+
+
+def save_examples(hosta_func=None, hosta_path=None):
+    cached_data = {}
+
+    if hosta_func is None:
+        try:
+            func_frame = inspect.currentframe().f_back
+            func_name = func_frame.f_code.co_name
+            func = func_frame.f_globals[func_name]
+        except:
+            raise ValueError(f"Please provide hosta_func for specifying the function")
+
+    elif callable(hosta_func):
+        func = hosta_func
+    else:
+        raise ValueError(f"Please provide hosta_func for specifying the function")
+
+    if hosta_path is None:
+        raise ValueError(
+            f"Please provide hosta_path for specifying the path to save the cache"
+        )
+    total_path = f"{hosta_path}" + ".jsonl"
+
+    func_name = func.__name__
+    path_name = os.path.join(CACHE_DIR, f"{func_name}.openhc")
+
+    try:
+        if os.path.exists(path_name):
+            with open(path_name, "rb") as f:
+                cached_data = pickle.load(f)
+                with open(total_path, "w") as t:
+                    for dict in cached_data["ho_example"]:
+                        t.write(json.dumps(dict) + "\n")
+        else:
+            raise ValueError(f"Could not found the cache at {path_name}")
+    except Exception as e:
+        raise ValueError(f"Could not found the cache at {path_name}") from e
+
+
+def load_examples(hosta_func=None, hosta_path=None):
+    if hosta_func is None:
+        try:
+            func_frame = inspect.currentframe().f_back
+            func_name = func_frame.f_code.co_name
+            func = func_frame.f_globals[func_name]
+        except:
+            raise ValueError(f"Please provide hosta_func for specifying the function")
+
+    elif callable(hosta_func):
+        func = hosta_func
+    else:
+        raise ValueError(f"Please provide hosta_func for specifying the function")
+
+    if hosta_path is None:
+        raise ValueError(
+            f"Please provide hosta_path for specifying the path to load the cache"
+        )
+
+    list_of_examples = []
+
+    try:
+        with open(hosta_path, "r") as file:
+            for line in file:
+                hosta_example = json.loads(line.strip())
+                list_of_examples.append(hosta_example)
+    except Exception:
+            raise IOError("Please provide a Json or a JsonL file only.")
+
+    cache_id = "ho_example"
+    value = list_of_examples
+    cache = Hostacache(func, cache_id, value)
+    cache()
diff --git a/src/OpenHosta/exec.py b/src/OpenHosta/exec.py
index 3f8d858..9af3266 100644
--- a/src/OpenHosta/exec.py
+++ b/src/OpenHosta/exec.py
@@ -1,46 +1,126 @@
+import pickle
+import os
+import hashlib
 import inspect
-import sys
-from typing import Callable, Any, Dict
+from typing import Callable, Dict, Any, get_origin, get_args
+import typing
+import collections
 from pydantic import BaseModel, create_model
+import sys
+import copy
 
 from .enhancer import enhance
 
 
-class HostaInjector:
+CACHE_DIR = "__hostacache__"
+os.makedirs(CACHE_DIR, exist_ok=True)
+
 
+class HostaInjector:
     def __init__(self, exec):
         if not callable(exec):
             raise TypeError("Executive function must be a function.")
+
         self.exec = exec
+        self.infos_cache = {}
 
     def __call__(self, *args, **kwargs):
-        infos = {"def": "", "call": "", "return_type": ""}
-
+        self.infos_cache = {
+            "hash_function": "",
+            "function_def": "",
+            "return_type": "",
+            "return_caller": "",
+            "function_call": "",
+            "function_locals": {},
+            "ho_example": [],
+            "ho_example_id": 0,
+            "ho_cothougt": [],
+            "ho_cothougt_id": 0,
+        }
         func_obj, caller = self._extend_scope()
-        infos["def"], func_prot = self._get_functionDef(func_obj)
-        infos["call"] = self._get_functionCall(func_obj, caller)
-        infos["return_type"] = self._get_functionReturnType(func_obj)
+        func_name = func_obj.__name__
+        path_name = os.path.join(CACHE_DIR, f"{func_name}.openhc")
+
+        if os.path.exists(path_name):
+            with open(path_name, "rb") as f:
+                cached_data = pickle.load(f)
+
+            function_def, func_prot = self._get_functionDef(func_obj)
+            function_hash = self._get_hashFunction(
+                function_def,
+                cached_data["ho_example_id"],
+                cached_data["ho_cothougt_id"],
+            )
 
-        self._attach_attributs(func_obj, func_prot)
-        return self.exec(
-            infos["def"], infos["call"], infos["return_type"], *args, **kwargs
+            if function_hash == cached_data["hash_function"]:
+                cached_data["function_call"], cached_data["function_locals"] = (
+                    self._get_functionCall(func_obj, caller)
+                )
+                self._attach_attributs(func_obj, func_prot)
+                return self.exec(cached_data, func_obj, *args, **kwargs)
+
+        hosta_args = self._get_argsFunction(func_obj)
+        with open(path_name, "wb") as f:
+            res = pickle.dump(hosta_args, f)
+        # TODO : fix the function locals because he didn't load in the cache
+        hosta_args["function_call"], hosta_args["function_locals"] = (
+            self._get_functionCall(func_obj, caller)
         )
+        return self.exec(hosta_args, func_obj, *args, **kwargs)
+
+    def _get_hashFunction(self, func_def: str, nb_example: int, nb_thought: int) -> str:
+        combined = f"{func_def}{nb_example}{nb_thought}"
+        return hashlib.md5(combined.encode()).hexdigest()
+
+    def _get_argsFunction(self, func_obj):
+
+        self.infos_cache["function_def"], func_prot = self._get_functionDef(func_obj)
+        self.infos_cache["return_type"], self.infos_cache["return_caller"] = (
+            self._get_functionReturnType(func_obj)
+        )
+        self.infos_cache["hash_function"] = self._get_hashFunction(
+            self.infos_cache["function_def"],
+            self.infos_cache["ho_example_id"],
+            self.infos_cache["ho_cothougt_id"],
+        )
+        self._attach_attributs(func_obj, func_prot)
+        return self.infos_cache
 
     def _extend_scope(self) -> Callable:
+        func: Callable = None
+
         try:
-            x = inspect.currentframe()
-            caller = x.f_back.f_back
-            name = caller.f_code.co_name
-            func = caller.f_globals.get(name)
+            current = inspect.currentframe()
+            if current is None:
+                raise Exception("Current frame is None")
+            caller_1 = current.f_back
+            if caller_1 is None:
+                raise Exception("Caller[lvl1] frame is None")
+            caller_2 = caller_1.f_back
+            if caller_2 is None:
+                raise Exception("Caller[lvl2] frame is None")
+
+            caller_name = caller_2.f_code.co_name
+
+            if "self" in caller_2.f_locals:
+                obj = caller_2.f_locals["self"]
+                func = getattr(obj, caller_name, None)
+            else:
+                code = caller_2.f_code
+                for obj in caller_2.f_back.f_locals.values():
+                    if hasattr(obj, "__code__"):
+                        if obj.__code__ == code:
+                            func = obj
+                            break
 
-            if func is None:
-                raise Exception("Scope can't be extend.")
             if not callable(func):
-                raise Exception("Larger scope isn't a callable.")
+                raise Exception(
+                    "Larger scope isn't a callable or scope can't be extended.\n"
+                )
         except Exception as e:
             sys.stderr.write(f"[FRAME_ERROR]: {e}")
-            return None
-        return func, caller
+            func, caller_2 = None, None
+        return func, caller_2
 
     def _get_functionDef(self, func: Callable) -> str:
         sig = inspect.signature(func)
@@ -66,11 +146,26 @@ def _get_functionDef(self, func: Callable) -> str:
         return definition, prototype
 
     def _get_functionCall(self, func: Callable, caller) -> str:
-        args, _, _, values = inspect.getargvalues(caller)
+        locals = None
+        _, _, _, values = inspect.getargvalues(caller)
 
         sig = inspect.signature(func)
-        bound_args = {}
-        bound_args = sig.bind_partial(**values)
+
+        values_args = copy.deepcopy(values)
+        values_locals = copy.deepcopy(values)
+        for values_name in values.keys():
+            if values_name not in sig.parameters.keys():
+                values_args.pop(values_name)
+            else:
+                values_locals.pop(values_name)
+
+        if "self" in values_locals.keys():
+            values_locals.pop("self")
+
+        if values_locals != {}:
+            locals = copy.deepcopy(values_locals)
+
+        bound_args = sig.bind_partial(**values_args)
         bound_args.apply_defaults()
 
         args_str = ", ".join(
@@ -79,7 +174,7 @@ def _get_functionCall(self, func: Callable, caller) -> str:
         )
 
         call = f"{func.__name__}({args_str})"
-        return call
+        return call, locals
 
     def _inspect_returnType(self, func: Callable) -> str:
         sig = inspect.signature(func)
@@ -89,26 +184,53 @@ def _inspect_returnType(self, func: Callable) -> str:
         else:
             return None
 
-    def _get_functionReturnType(self, func: Callable) -> Dict[str, Any]:
-        return_type = self._inspect_returnType(func)
-        return_json = None
+    def _get_typingOrigin(self, return_type) -> bool:
+        origin = get_origin(return_type)
+        return origin in {
+            list,
+            dict,
+            tuple,
+            set,
+            frozenset,
+            typing.Union,
+            typing.Annotated,
+            typing.Optional,
+            typing.Literal,
+            collections.deque,
+            collections.abc.Iterable,
+            collections.abc.Sequence,
+            collections.abc.Mapping,
+        }
 
-        if return_type is not None:
-            if issubclass(return_type, BaseModel):
-                return_json = return_type.model_json_schema()
+    def _get_functionReturnType(self, func: Callable) -> Dict[str, Any]:
+        return_caller = self._inspect_returnType(func)
+        return_type = None
+
+        if return_caller is not None:
+            if self._get_typingOrigin(return_caller):
+                return_caller_origin = get_origin(return_caller)
+                return_caller_args = get_args(return_caller)
+                combined = return_caller_origin[return_caller_args]
+                new_model = create_model(
+                    "Hosta_return_shema", return_hosta_type_typing=(combined, ...)
+                )
+                return_type = new_model.model_json_schema()
+            elif issubclass(return_caller, BaseModel):
+                return_type = return_caller.model_json_schema()
             else:
                 new_model = create_model(
-                    "Hosta_return_specified", return_hosta_type=(return_type, ...)
+                    "Hosta_return_shema", return_hosta_type=(return_caller, ...)
                 )
-                return_json = new_model.model_json_schema()
+                return_type = new_model.model_json_schema()
         else:
             No_return_specified = create_model(
-                "Hosta_return_no_specified", return_hosta_type=(Any, ...)
+                "Hosta_return_shema", return_hosta_type_any=(Any, ...)
             )
-            return_json = No_return_specified.model_json_schema()
+            return_type = No_return_specified.model_json_schema()
 
-        return return_json
+        return return_type, return_caller
 
     def _attach_attributs(self, func: Callable, prototype: str):
-        func.__suggest__ = enhance
-        func._prot = prototype
+        if "bound method" not in str(func):
+            setattr(func, "__suggest__", enhance)
+            setattr(func, "_prot", prototype)
diff --git a/src/OpenHosta/prompt.json b/src/OpenHosta/prompt.json
index 7b2fb6c..ec4ba33 100644
--- a/src/OpenHosta/prompt.json
+++ b/src/OpenHosta/prompt.json
@@ -1,22 +1,28 @@
 {
     "prompts": [
         {
-            "key": "emulate",
-            "text": "You will act as an emulator of impossible-to-code functions. I will provide you with the description of the function using Python's way of declaring functions, but I won't provide the function body as I don't know how to code it. It might even be impossible to code. Therefore, you should not try to write the body. Instead, directly imagine the function output.\n\nIn the conversation, I will directly write the function call as if it was called in Python. You should directly answer with whatever you believe would be a good return for the function.\n\nWhen you produce an answer, you should estimate the confidence level:\n\n    \"low\": You did your best, but with the provided description and your knowledge, you are not confident about the answer.\n    \"medium-instance\": You did your best, and with the provided description and your knowledge, you are pretty sure this answer is valid but not the only valid answer.\n    \"medium-unique\": You did your best, and with the provided description and your knowledge, you are pretty sure this answer is the unique and valid answer.\n    \"high-instance\": You did your best, and you are sure that your provided answer is a valid answer. It is a well-known function or you can easily implement a Python code that yields elements from the list of valid answers. This answer is randomly chosen from the list of valid answers.\n    \"high-unique\": You did your best, and you are sure that your provided answer is the unique valid answer. It is a well-known function or you can easily implement a Python code that solves the question and calculates this answer given this input.\n\nIf the output is documented as a Python structure, you should translate it to JSON.\nYou should encode the return in valid JSON format, without comments, using the following format:\n{\"return\":..., \"confidence\":...}\n\nThe output must be of the same type as that specified in the function call.\n\nAny assumptions made should be reasonable based on the provided function description and should take into account the error handling of the function.\n\nConsistency in the confidence levels is important to ensure accurate responses.\nThis is the function documentation:\n\nExample function call:\n\ndef example_function(a: int, b: dict) -> int:\n    \"\"\"\n    \"\"\"\n    This is an example function.\n    It adds two numbers.\n    \"\"\"\n    \"\"\"\n    pass\n\nExample imagined function output:\n\nresult = example_function(3, {\"value\": 7})\n\nExpected JSON output:\n\n{\"return\": 10, \"confidence\": \"medium-unique\"}\n\nThis is the function documentation:",
+            "key": "estimate",
+            "text": "You're a prompt engineering engineer tasked with estimating the number of output tokens an AI would return when executing a given function.\nThe functions are written in Python, so function returns must use Python typing.\n\nEach time, I'll give you the following elements:\n- The definition of the function.\n- Its call with arguments.\n- The function's docstring.\n\nYou need to take all these elements into account when formulating your answer.\nTo estimate the output token, you need to use a tokenization algorithm: take the one in GPT-3.\n---\nTo make your estimate, you need to go through this chain of thought:\n\n1. Understand the Function.\n\t- With the given definition, the function prototype and its docstring.\n2. Analyze the Function Call.\n\t- With the function name and arguments provided.\n3. Guess the expected result without calculating it.\n\t- You MUST NOT calculate the result of the function.\n\t- Simply make a prediction with all the elements you have at your disposal.\n\t- Pay attention to the type of output. If it's not specified in the function prototype, then guess it based on the description and type of the input arguments.\n4. Estimate the number of tokens.\n\t- Use the information you have available for this step.\n\t- If the estimate is complex or impossible, make a realistic prediction using the context elements.\n5. Formulate your answer\n\t- Synthesize your answer into a single number.\n\t- Follow the answer format I'll give you below\n---\nYou should encode your response in valid JSON format, without comments, using the following format:\n{ \"tokens\":...}\nYour answer in the \"tokens\" category must be a number only.\nNothing should appear other than this JSON structure.\n\nAny assumptions made should be reasonable based on the provided function description and should take into account the error handling of the function.\n---\nI'll give you an example:\nFunction definition:\n```python\ndef reverse_string(a:str)->str:\n\t\"\"\"\n\tThis function reverse the string in parameter.\n\t\"\"\"\n\treturn emulate()\n```\n\nFunction call:\n```python\nreverse_string(\"Hello World!\")\n```\n\nExcpected output:\n```\n{\"tokens\": 13}\n```\n\nHere's all the function documentation for you to estimate:\n---\n",
+            "category": "analytics",
+            "version": "v1.1"
+        },
+        {
+            "key": "thought",
+            "text": "You will act as an emulator of impossible-to-code functions. I will provide you with the description of the function using Python's way of declaring functions, but I won't provide the function body as I don't know how to code it. It might even be impossible to code. Therefore, you should not try to write the body. Instead, directly imagine the function output.\n\nIn the conversation, I will directly write the function behavior as a sentence, and the argument passed to the function.\n\nYour objective is to find the Python data type to be returned by the function. Take into account the function's behavior, the wording and intent of the sentence, and the arguments given. You must give your answer without any comment and in the following JSON schema:\n```\n{\"type\": \"\"}\n```\n\nTo fill in the type key, you need to follow Python syntax, such as \"int\" or \"str\", depending on your answer. \n\n---\n\nHere are a few examples:\n\nFunction behavior: \"Is a positive number\"\nArgument: 2\nExpected response: {\"type\": \"bool\"}\n\nFunction behavior: \"Multiply a number by 2\"\nArgument: 10\nExpected response: {\"type\": \"int\"}\n\nFunction behavior: \"Reverse a string\"\nArgument: \"Hello World!\"\nExpected response: {\"type\": \"str\"}\n\nFunction behavior: \"Sorts a list in ascending order\"\nArgument: (10, 5, 7, 12, 3)\nExpected response: {\"type\": \"list\"}\n\n---\n",
             "category": "executive",
-            "version": "v1.0"
+            "version": "1.1"
         },
         {
             "key": "enhance",
-            "text": "I want you to become my Expert Prompt Creator for developer. Your goal is to help me craft the best possible prompt for my programming needs. The prompt you provide should be written from the perspective of me making the request to GPT-4o. Consider in your prompt creation that this prompt will be entered into an interface for GPT-4o. Apart from diagrams, you must write in text, without following any syntax. The process is as follows: You will generate the following sections:\n -> enhanced:\n{provide the best possible prompt according to my request. The prompt is used to describe a function to be performed in Python as precisely as possible. You can add error handling, as the function needs it to work properly. But don't code the function in the prompt. The prompt should not tell to create the function, but describe how it works.}\n-> review:\n{provide a concise paragraph on how to improve the prompt. Be very critical in your response. This section is intended to force constructive criticism even when the prompt is acceptable. Any assumptions and or issues should be included. Don't forget that you speak to a developer}\n-> advanced:\n{rewrite the prompt with the suggested improvements you made in the critique category. The aim is to make a proposal, an example. Make logical assumptions to solve the problem based on the context and all the information you have. You have to respond to the problems you formulated in the previous category. But don't code the function in the prompt.}\n-> mermaid:\n{Make a mermaid diagram explaining the function described by the prompt. You need to break the reasoning into several steps for ease of understanding and clarity. You must make it logical and easy to look at. You have to write it in mermaid syntax. You must not use the markdown syntax}\n",
+            "text": "I want you to become my Expert Prompt Creator for developer. Your goal is to help me craft the best possible prompt for my programming needs. The prompt you provide should be written from the perspective of me making the request to GPT-4o. Consider in your prompt creation that this prompt will be entered into an interface for GPT-4o. Apart from diagrams, you must write in text, without following any syntax. The process is as follows: You will generate the following sections:\n\n---\n\n -> enhanced:\n{provide the best possible prompt according to my request. The prompt is used to describe a function to be performed in Python as precisely as possible. You can add error handling, as the function needs it to work properly. But don't code the function in the prompt. The prompt should not tell to create the function, but describe how it works.}\n\n-> review:\n{provide a concise paragraph on how to improve the prompt. Be very critical in your response. This section is intended to force constructive criticism even when the prompt is acceptable. Any assumptions and or issues should be included. Don't forget that you speak to a developer}\n\n-> advanced:\n{rewrite the prompt with the suggested improvements you made in the critique category. The aim is to make a proposal, an example. Make logical assumptions to solve the problem based on the context and all the information you have. You have to respond to the problems you formulated in the previous category. But don't code the function in the prompt.}\n\n-> mermaid:\n{Make a mermaid diagram explaining the function described by the prompt. You need to break the reasoning into several steps for ease of understanding and clarity. You must make it logical and easy to look at. You have to write it in mermaid syntax. You must not use the markdown syntax}\n\n---\n\nYou must formulate your response without any comment and in this JSON schema:\n```\n{\"enhanced\": \"\", \"review\": \"\", \"advanced\": \"\", \"mermaid\": \"\"}\n```\n---\n",
             "category": "analytics",
-            "version": "v1.0"
+            "version": "v1.2"
         },
         {
-            "key": "estimate",
-            "text": "You're a prompt engineering engineer tasked with estimating the number of output tokens an AI would return when executing a given function.\nThe functions are written in Python, so function returns must use Python typing.\n\nEach time, I'll give you the following elements:\n- The definition of the function.\n- Its call with arguments.\n- The function's docstring.\n\nYou need to take all these elements into account when formulating your answer.\nTo estimate the output token, you need to use a tokenization algorithm: take the one in GPT-3.\n\nTo make your estimate, you need to go through this chain of thought:\n\n1. Understand the Function.\n\t- With the given definition, the function prototype and its docstring.\n2. Analyze the Function Call.\n\t- With the function name and arguments provided.\n3. Guess the expected result without calculating it.\n\t- You MUST NOT calculate the result of the function.\n\t- Simply make a prediction with all the elements you have at your disposal.\n\t- Pay attention to the type of output. If it's not specified in the function prototype, then guess it based on the description and type of the input arguments.\n4. Estimate the number of tokens.\n\t- Use the information you have available for this step.\n\t- If the estimate is complex or impossible, make a realistic prediction using the context elements.\n5. Formulate your answer\n\t- Synthesize your answer into a single number.\n\t- Follow the answer format I'll give you below\n\nYou should encode your response in valid JSON format, without comments, using the following format:\n{ “tokens”:...}\nYour answer in the “tokens” category must be a number only.\nNothing should appear other than this JSON structure.\n\nAny assumptions made should be reasonable based on the provided function description and should take into account the error handling of the function.\n\nI'll give you an example:\nFunction definition:\n```python\ndef reverse_string(a:str)->str:\n\t\"\"\"\n\tThis function reverse the string in parameter.\n\t\"\"\"\n\treturn emulate()\n```\n\nFunction call:\n```python\nreverse_string(\"Hello World!\")\n```\n\nExcpected output:\n```\n{\"tokens\": 13}\n```\n\nHere's all the function documentation for you to estimate:",
-            "category": "analytics",
-            "version": "v1.0"
+            "key": "emulate",
+            "text": "You will act as an emulator of impossible-to-code functions. I will provide you with the description of the function using Python's way of declaring functions, but I won't provide the function body as I don't know how to code it. It might even be impossible to code. Therefore, you should not try to write the body. Instead, directly imagine the function output.\n\nIn the conversation, I will directly write the function call as if it was called in Python. You should directly answer with whatever you believe would be a good return for the function.\n\nIf the output is documented as a Python structure, you should translate it to JSON.\nYou should encode the return in valid JSON format, without comments, using the following format:\n```\n{\"return\":\"...\"}\n```\nThe output must be of the same type as that specified in the function call.\n\nAny assumptions made should be reasonable based on the provided function description and should take into account the error handling of the function.\n\nConsistency in the confidence levels is important to ensure accurate responses.\nThis is the function documentation:\n---\nExample function definition:\n```python\ndef example_function(a: int, b: dict) -> int:\n\t\"\"\"\n\tThis is an example function.\n\tIt adds two numbers.\n\t\"\"\"\n\treturn emulate()\n```\n\nExample imagined function call:\n```python\nresult = example_function(3, {\\\"value\\\": 7})\n```\n\nExpected JSON output:\n{\"return\": 10}\n---\n",
+            "category": "executive",
+            "version": "v1.2"
         }
     ]
-}
+}
\ No newline at end of file
diff --git a/src/OpenHosta/prompt.py b/src/OpenHosta/prompt.py
index 88dfddb..a6be5cd 100644
--- a/src/OpenHosta/prompt.py
+++ b/src/OpenHosta/prompt.py
@@ -39,3 +39,59 @@ def get_prompt_details(self, key):
             return prompt
         sys.stderr.write(f"[JSON_ERROR] Prompt not found\n")
         return None
+
+    def set_prompt(self, name: str, category: str, version: str, filepath: str):
+        json_filepath = "prompt.json"
+        new = {"key": "", "text": "", "category": "", "version": ""}
+
+        try:
+            with open(filepath, "r", encoding="utf-8") as file:
+                prompt = file.read()
+        except FileNotFoundError:
+            sys.stderr.write(f"File {filepath} not found.")
+        except IOError as e:
+            sys.stderr.write(f"File error: {e}")
+        else:
+            try:
+                with open(json_filepath, "r", encoding="utf-8") as json_file:
+                    data = json.load(json_file)
+            except FileNotFoundError:
+                sys.stderr.write(f"File {json_filepath} not found.")
+            except IOError as e:
+                sys.stderr.write(f"File error: {e}")
+            except Exception as e:
+                sys.stderr.write(e)
+            else:
+                new["key"], new["category"], new["version"] = name, category, version
+                new["text"] = prompt
+
+                found = False
+                for elem in data["prompts"]:
+                    if elem["key"] == new["key"]:
+                        elem["text"] = new["text"]
+                        elem["version"] = new["version"]
+                        found = True
+                        break
+
+                if not found:
+                    data["prompts"].append(new)
+
+                try:
+                    with open(json_filepath, "w", encoding="utf-8") as json_file:
+                        json.dump(data, json_file, ensure_ascii=False, indent=4)
+                except FileNotFoundError:
+                    sys.stderr.write(f"File {json_filepath} not found.")
+                except IOError as e:
+                    sys.stderr.write(f"File error: {e}")
+                except Exception as e:
+                    sys.stderr.write(e)
+                else:
+                    print(f"{name} prompt has been added to {json_filepath}")
+
+    def show_prompt(self, key):
+        prompt = self.prompts.get(key)
+        if prompt:
+            print(prompt["text"])
+            return prompt["text"]
+        sys.stderr.write(f"[JSON_ERROR] Prompt not found\n")
+        return None
diff --git a/src/OpenHosta/thought.py b/src/OpenHosta/thought.py
new file mode 100644
index 0000000..7547f73
--- /dev/null
+++ b/src/OpenHosta/thought.py
@@ -0,0 +1,75 @@
+import sys
+import json
+from pydoc import locate
+from pydantic import create_model
+
+from .emulate import _exec_emulate
+from .config import DefaultManager
+from .prompt import PromptMananger
+
+l_default = DefaultManager.get_default_model()
+_x = PromptMananger()
+_thought_sys_prompt = _x.get_prompt("thought")
+
+
+def thought(key):
+    _function_infos = {
+        "function_def": "",
+        "function_call": "",
+        "return_type": None,
+        "return_type": None,
+        "ho_example": None,
+        "function_locals": None,
+        "return_caller": None,
+    }
+
+    def inner_func(*args, **kwargs):
+        global l_default, _thought_sys_prompt
+
+        l_user_prompt = (
+            "Here's the function behavior:\n"
+            + f"{key}\n"
+            + "Here's the arguments:\n"
+            + f"{args}\n"
+        )
+
+        response = l_default.api_call(
+            sys_prompt=_thought_sys_prompt,
+            user_prompt=l_user_prompt,
+            creativity=0.5,
+            diversity=0.5,
+        )
+
+        data = response.json()
+        type_json = data["choices"][0]["message"]["content"]
+        type_dict = json.loads(type_json)
+        type_str = str(type_dict["type"])
+
+        _function_infos["return_caller"] = locate(type_str)
+        setattr(inner_func, "_return_type", _function_infos["return_caller"])
+
+        new_model = create_model(
+            "Hosta_return_shema",
+            return_hosta_type=(_function_infos["return_caller"], ...),
+        )
+        _function_infos["return_type"] = new_model.model_json_schema()
+
+        typed = (
+            str(args)
+            + "\n"
+            + 'Here\'s the return type that must respect for your response. The python type is in the key "type" of this JSON schema:\n'
+            + str(type_dict)
+            + "\n"
+        )
+
+        try:
+            _function_infos["function_def"] = key
+            _function_infos["function_call"] = typed
+            result = _exec_emulate(_function_infos, inner_func)
+        except Exception as e:
+            sys.stderr.write(f"{e}")
+            sys.stderr.write("[LMDA_ERROR]")
+            result = None
+        return result
+
+    return inner_func


Test patch:
diff --git a/tests/.gitkeep b/tests/data/.gitkeep
similarity index 100%
rename from tests/.gitkeep
rename to tests/data/.gitkeep
diff --git a/tests/functionnalTests/test_mandatory.py b/tests/functionnalTests/test_mandatory.py
new file mode 100644
index 0000000..df53491
--- /dev/null
+++ b/tests/functionnalTests/test_mandatory.py
@@ -0,0 +1,296 @@
+import pytest
+import time as t
+import sys
+import os
+from pydantic import BaseModel
+from typing import Callable
+
+from OpenHosta import config, emulate, thought
+
+g_apiKey = ""
+
+config.set_default_apiKey(g_apiKey)
+
+def emulate_1arg_str(arg)->str:
+    """ Docstring """
+    return emulate()
+
+def emulate_1arg_int(arg)->int:
+    """ Docstring """
+    return emulate()
+
+def emulate_1arg_float(arg)->float:
+    """ Docstring """
+    return emulate()
+
+def emulate_1arg_list(arg)->list:
+    """ Docstring """
+    return emulate()
+
+emulate_1arg = {"str": emulate_1arg_str, "int": emulate_1arg_int, "float": emulate_1arg_float, "list": emulate_1arg_list}
+
+class User(BaseModel):
+    name: str
+    email: str
+    age: int
+
+class TestEmulate:
+       
+    def test_FeatureModelInParameter(self):
+        global g_apiKey
+        
+        abracadabra = config.Model(
+            model="gpt-4o-mini",
+            base_url="https://api.openai.com/v1/chat/completions",
+            api_key=g_apiKey
+        )
+        
+        def randomSentence()->str:
+            """
+            This function returns a random sentence.
+            """
+            return emulate(model=abracadabra)
+        
+        ret = randomSentence()
+        ret_model = randomSentence._last_response["model"]
+        print(ret_model)
+        assert "gpt-4o-mini" in ret_model   
+       
+    @pytest.mark.parametrize("type, name, doc, arg, expected", [
+        ("str", "generator", "generates a sentence", "", str),
+        ("int", "generator", "generates an integer", "", int),
+        ("list", "generator", "generates a list in python", "", list),
+        ("float", "generator", "generates a float in python", "", float),
+    ])
+    def test_basicType(self, type, name, doc, arg, expected):
+        global emulate_1arg
+        emulate_1arg[type].__name__ = name
+        emulate_1arg[type].__doc__ = doc
+        my_func = emulate_1arg[type]
+        result = my_func(arg)
+        print(f"[{doc}]: {result}")
+        assert isinstance(result, expected)
+
+
+    def test_FeaturesEmptyInfos(self):
+        def a():
+            return emulate()
+        res = a()
+        assert res is None
+        
+    def test_FeatureDecorator(self):
+        
+        def decorator(func):
+            def wrapper(*args, **kwargs):
+                res = func(*args, **kwargs)
+                return res
+            return wrapper
+        
+        @decorator
+        def returntNone():
+            """ This function returns None """
+            return emulate()
+        
+        ret = returntNone()
+        assert ret is None
+        
+    def test_FeatureClassMethod(self):
+        
+        class EmulateClass:
+            
+            def returntThree(self)->int:
+                """ This function returns 3 """
+                return emulate()
+            
+        x = EmulateClass()
+        ret = x.returntThree()
+        assert ret == 3
+        
+    def test_FeatureClassMagicMethod(self, capsys):
+        
+        class EmulateClass:
+            
+            def __str__(self):
+                """ This function returns "Hello World!" """
+                return emulate()
+            
+        x = EmulateClass()
+        print(x)
+        stdout = capsys.readouterr()
+        assert "Hello World!" in stdout.out
+        
+    def test_FeatureLocalVariables(self):
+        
+        def showLocals()->dict:
+            """
+            This function return in a dict all his locals variable and arguments.
+            """
+            local_1 = 42
+            local_2 = "Hello World!"
+            local_3 = [1, 2, 3, 4, 5]
+            return emulate()
+        
+        ret_dict = {
+            "local_1": 42,
+            "local_2": "Hello World!",
+            "local_3": [1, 2, 3, 4, 5]
+        }
+        ret = showLocals()
+        assert ret == ret_dict
+      
+    def test_FeatureNestedFunction(self):
+            
+            def grandFunction():
+                def returnHelloWorld():
+                    """ 
+                    This function returns "Hello World!".
+                    """
+                    return emulate()
+                return returnHelloWorld
+            
+            nested = grandFunction()
+            ret = nested()
+            assert ret == "Hello World!" 
+        
+    def test_FeaturePydanticGlobal(self):
+            
+        def generateAccount(name:str, email:str, age:int)->User:
+            """
+            This function generates a customer account with information based on the arguments passed, and returns it to the pydantic “User” model.
+            """
+            return emulate()
+        
+        ret = generateAccount("Max", "maxexample@gmail.com", 21)
+        ex = User(name="Max", email="maxexample@gmail.com", age=18)
+        assert type(ret) is User
+        
+    def test_FeaturePydanticLocal(self):
+        pass
+        
+    def test_FeatureSuggestDefaultModel(self):
+        
+        my_model = config.Model(
+            model="gpt-4o-mini",
+            base_url="https://api.openai.com/v1/chat/completions",
+            api_key=g_apiKey
+        )
+        
+        config.set_default_model(my_model)
+        
+        def multiply(a:int, b:int)->int:
+            """
+            This function multiplies two integers in parameter.
+            """
+            return emulate()
+        
+        ret = multiply(2, 3)
+        ret_suggest = multiply.__suggest__(multiply)
+        ret_model = multiply._last_response["model"]
+        
+        assert isinstance(ret_suggest, dict)
+        assert "gpt-4o-mini" in ret_model
+        assert not multiply.advanced is None
+        assert not multiply.enhanced_prompt is None
+        assert not multiply.review is None
+        assert not multiply.diagramm is None
+
+class TestThought:
+    
+    def test_BasicDirect(self):
+        ret = thought("Multiply by 2")(8)
+        assert ret == 16
+    
+    def test_BasicIndirect(self):
+        x = thought("Translate in English")
+        ret = x("Bonjour Monde!")
+        assert isinstance(x, Callable)
+        assert ret == "Hello World!"
+
+    @pytest.mark.parametrize("prompt, args, expected", [
+        ("return a random integer", "", int),
+        ("return a random sentence", "", str),
+        ("return a random float", "", float),
+        ("return list with 5 random integers", "", list),
+        ("return a random bool in python", "", bool),
+    ])
+    def test_BasicTyped(self, prompt, args, expected):
+        ret = thought(prompt)(args)
+        assert isinstance(ret, expected)
+        
+    @pytest.mark.parametrize("prompt, args, expected", [
+        ("Count the letter un a setence", "Hello World!", int),
+        ("capitalize a setence", "hello world!", str),
+        ("Give the number Pi to 3 decimal places", "", float),
+        ("Sort in ascending order", [2, 5, 1, 12, 6], list),
+        ("Is a positive number", 6, bool),
+    ])
+    def test_FeaturePredict(self, prompt, args, expected):
+        x = thought(prompt)
+        ret = x(args)
+        assert x._return_type is expected
+
+    def test_FeatureMultiArgs(self):
+        x = thought("Combine each word of a sentence in a string")
+        ret = x("Hello", ", how are you ?", " Nice to meet you !")
+        assert ret == "Hello, how are you ? Nice to meet you !"
+    
+    def test_FeatureChainOfThought(self):
+        pass
+    
+    def test_FeatureDefaultModel(self):
+        my_model = config.Model(
+            model="gpt-4o-mini",
+            base_url="https://api.openai.com/v1/chat/completions",
+            api_key=g_apiKey
+        )
+        
+        config.set_default_model(my_model)
+        
+        x = thought("Is a masculine name")
+        ret = x("Max")
+        print(x._last_response)
+        ret_model = x._last_response["model"]
+        assert ret == True
+        assert "gpt-4o-mini" in ret_model
+    
+    def test_FeatureCachedPrediction(self):
+        pass
+
+
+class TestTypingPydantic:
+    pass
+
+"""
+typing (parametrize)
+creativity, diversity
+example
+cache
+---
+test en trompant le LLM dans la def ou la doc
+test faute d'rthographe
+Error:
+quand func n'est pas trouvé, Erreur expliquant la déclaration dans le frame parent
+def test():
+    def a():
+        \"\"\" Give me a ramdom word \"\""
+        return emulate()
+    return a
+
+#Erreur
+print(test()())
+#Valid
+b = test()
+print(b())
+
+CONTRIBUTING
+changelog
+doc
+test
+---
+Makefile
+make benchmark
+make functests
+make unittests
+avec redirection de coverage et html dans TestData
+argument makefile apiKey
+"""
\ No newline at end of file
diff --git a/tests/performanceTests/benchmark_GPT4o.py b/tests/performanceTests/benchmark_GPT4o.py
new file mode 100644
index 0000000..d97565f
--- /dev/null
+++ b/tests/performanceTests/benchmark_GPT4o.py
@@ -0,0 +1,9 @@
+node = {"name": "", "duration": -1, "score": True}
+test_info = []
+
+def add_to_benchmark(name:str, duration:float, score:bool):
+    global node, test_info
+    new = dict(node)
+    new["name"], new["duration"], new["score"] = name, duration, score
+    test_info.append(new)
+    return test_info
\ No newline at end of file
diff --git a/tests/performanceTests/test_emulate_default.py b/tests/performanceTests/test_emulate_default.py
new file mode 100644
index 0000000..3626c01
--- /dev/null
+++ b/tests/performanceTests/test_emulate_default.py
@@ -0,0 +1,152 @@
+import pytest
+import time as t
+import sys
+import os
+
+sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "src")))
+
+from OpenHosta import emulate, config
+
+config.set_default_apiKey("")
+
+
+def timer_tests(func):
+    def wrapper(*args, **kwargs):
+        g_c = "\033[94m"
+        n = "\033[0m"
+        bold = "\033[1m"
+
+        start = t.time()
+        rv = func(*args, **kwargs)
+        end = t.time()
+
+        duration = end - start
+        print(f"{g_c}{bold}Execution time of {func.__name__}: {duration:.2f}s{n}")
+        return (rv, duration)
+
+    return wrapper
+
+
+@timer_tests
+def emulate_1args_int(arg) -> int:
+    """Docstring"""
+    return emulate()
+
+
+@timer_tests
+def emulate_2args_int(arg1, arg2) -> int:
+    """Docstring"""
+    return emulate()
+
+
+class TestEmulate:
+
+    @pytest.mark.parametrize(
+        "name, doc, int1, int2, expected",
+        [
+            (
+                "multiply",
+                "This function mutliplies two integers in parameters",
+                2,
+                3,
+                6,
+            ),
+            (
+                "add", 
+                "This function adds two integers in parameters", 
+                2, 
+                3, 
+                5
+            ),
+            (
+                "multiply_float",
+                "This function multiplies two floating-point numbers in parameter.",
+                3.8,
+                7.7,
+                29.26,
+            ),
+            (
+                "power",
+                "This function calculates the power of a number given a base (first arg) and an exponent (second arg).",
+                4,
+                5,
+                1024,
+            ),
+            (
+                "sum_list",
+                "This function adds an integers in parameter to all integers in a list in parameter",
+                10,
+                [1, 6, 7, 12, 0],
+                [11, 16, 17, 22, 10],
+            ),
+            (
+                "substract",
+                "This function substracts two integers in parameters",
+                2,
+                3,
+                -1,
+            ),
+            (
+                "divide",
+                "This function divides the first integers in parameter by the second",
+                621,
+                3,
+                207,
+            ),
+        ],
+    )
+    def test_math_basicOperation(self, name, doc, int1, int2, expected):
+        emulate_2args_int.__name__ = name
+        emulate_2args_int.__doc__ = doc
+        result, duration = emulate_2args_int(int1, int2)
+        assert result == expected
+
+    @pytest.mark.parametrize(
+        "name, doc, int1, int2, expected",
+        [
+            (
+                "factorial",
+                "This function calculates the factorial of a non-negative integer in parameter",
+                10,
+                -1,
+                3628800,
+            ),
+            (
+                "common_divisor",
+                "This function returns the greatest common divisor of two integers in parameter.",
+                36,
+                60,
+                12,
+            ),
+            (
+                "square_root",
+                "This function computes the square root of a given non-negative number in parameter.",
+                144,
+                -1,
+                12,
+            ),
+            (
+                "is_prime",
+                "This function determines if a given integer is a prime number. Return 0 if true and 1 if false",
+                199,
+                -1,
+                0,
+            ),
+        ],
+    )
+    def test_math_mediumOperation(self, name, doc, int1, int2, expected):
+        if int2 == -1:
+            emulate_1args_int.__name__ = name
+            emulate_1args_int.__doc__ = doc
+            result, duration = emulate_1args_int(int1)
+        else:
+            emulate_2args_int.__name__ = name
+            emulate_2args_int.__doc__ = doc
+            result, duration = emulate_2args_int(int1, int2)
+        assert result == expected
+
+
+# high number
+# negative
+# hard division
+# duration test
diff --git a/tests/unitTests/unitTest.py b/tests/unitTests/unitTest.py
new file mode 100644
index 0000000..e69de29