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

Issue:
Refacto merge



Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 819a5ca..31d03f4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,4 +16,5 @@ __hostacache__/
 src/OpenHosta.egg-info
 build/
 .pypirc
-testtest.py
\ No newline at end of file
+testtest.py
+tiling.py
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5c3084f..8e32504 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,11 +23,17 @@ All significant changes to this project will be documented in this file.
 ## **v2.0-beta1**
 
 - **Features**
-  - All the attributs are now attach to methods
+  - Added `max_tokens` args for emulate
+  - Added `use_locals/self_as_ctx` args for emulate for clarity and modularity
+  - `thought` is now used to add chain of thoughts in a emulated function
+  - `PromptManager` is now available for users to change all meta-prompt.
+
+- **Changes**
+  - `thought` function become `thinkof`
+  - `suggest` is removed
+  - `creativity` become `temperature` and `diversity` become `top_p`
+  - There's no more `hostacache` for emulated functions
 
-- **Internal**
-  - Added `py.typed` file for linter
-  - Refacto the project's structure
 
 ## **v1.2.0** - 10/14/2024
 
diff --git a/DEPRECATED/OpenHosta.py b/DEPRECATED/OpenHosta.py
deleted file mode 100644
index f2bca6a..0000000
--- a/DEPRECATED/OpenHosta.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from .core.config import Model, DefaultManager
-
-DefaultManager.set_default_model(
-    Model(model="gpt-4o", base_url="https://api.openai.com/v1/chat/completions")
-)
-
-from .core.emulate import _exec_emulate
-from .predict.predict import _exec_predict
-from .predict.trainset import TrainingSet
-from .core import config
-from .body.thought import thought
-from .core.exec import HostaInjector
-from .body.example import example, save_examples, load_training_example
-from .body.enhancer import suggest
-
-emulate = HostaInjector(_exec_emulate)
-predict = HostaInjector(_exec_predict)
-
-__all__ = (
-    "emulate",
-    "thought",
-    "example", 
-    "save_examples",
-    "load_training_example",
-    "TrainingSet",
-    "config", 
-    "Model", 
-    "DefaultManager",
-    "suggest",
-    "predict"
-)
-
diff --git a/DEPRECATED/__init__.py b/DEPRECATED/__init__.py
deleted file mode 100644
index 589b87c..0000000
--- a/DEPRECATED/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from .OpenHosta import *
-
-__version__ = "1.3.0"
diff --git a/DEPRECATED/body/enhancer.py b/DEPRECATED/body/enhancer.py
deleted file mode 100644
index b9ad155..0000000
--- a/DEPRECATED/body/enhancer.py
+++ /dev/null
@@ -1,100 +0,0 @@
-import json
-import sys
-from typing import Callable
-
-from ..utils.prompt import PromptMananger
-from ..core.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 l_default
-
-    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"
-    )
-
-    response = l_default.api_call(
-        sys_prompt=sys_prompt, user_prompt=l_user_prompt, creativity=0.8, diversity=0.8
-    )
-
-    response_data = response.json()
-    return response_data["choices"][0]["message"]["content"]
-
-
-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)
-
-    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__ or not type(func.__name__) is str:
-            raise ValueError("ValueError -> function name")
-        if not last_enh["enhanced"] or not type(last_enh["enhanced"]) is str:
-            raise ValueError("ValueError -> enhanced output")
-        if not last_enh["review"] or not type(last_enh["review"]) is str:
-            raise ValueError("ValueError -> review output")
-        if not last_enh["advanced"] or not type(last_enh["advanced"]) is str:
-            raise ValueError("ValueError -> seggested output")
-        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}")
-        return -1
-    finally:
-        func.enhanced_prompt = last_enh["enhanced"]
-        func.review = last_enh["review"]
-        func.advanced = last_enh["advanced"]
-        func.diagram = last_enh["mermaid"]
-    return 0
-
-
-def enhance(func):
-    global _enhancer_pre_prompt
-
-    last_enh: dict = {
-        "enhanced": None,
-        "review": None,
-        "advanced": None,
-        "mermaid": None,
-    }
-
-    func_name, func_doc = func.__name__, func.__doc__
-
-    last_return = _ai_call_enh(_enhancer_pre_prompt, func._prot, func_doc)
-
-    last_enh = _parse_data(last_return, last_enh)
-
-    _build_attributes(func, last_enh)
-    return last_enh
-
-def suggest(func:Callable):
-    if not callable(func):
-        raise ValueError("Suggest arguments must be a callable.")
-    try:
-        full = func.__suggest__(func)
-    except AttributeError:
-        raise AttributeError(f"The “__suggest__” attribute is not defined. The function {func.__name__} might not have been called.")
-    return full
diff --git a/DEPRECATED/body/example.py b/DEPRECATED/body/example.py
deleted file mode 100644
index db19a06..0000000
--- a/DEPRECATED/body/example.py
+++ /dev/null
@@ -1,255 +0,0 @@
-import inspect
-import pickle
-import os
-import json
-import csv
-from typing import Callable
-
-from ..utils.errors import FrameError
-from ..utils.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, _ = _extend_scope()
-        except:
-            raise ValueError("Please provide hosta_func for specifying the function")
-    elif callable(hosta_func):
-        func = hosta_func
-    else:
-        raise ValueError("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("Function does not have a signature")
-
-    type_verificator(args, kwargs, input_type, output_type, hosta_out, func, example_dict)
-
-    cache_id = "ho_example"
-    cache = Hostacache(func, cache_id, example_dict)
-    cache.create_hosta_cache()
-
-
-def type_verificator(args, kwargs, input_type, output_type, hosta_out, func, example_dict):
-    """ 
-    Validates the types of both positional and keyword arguments, as well as the return value.
-    """
-    
-    if args:
-        if len(args) != len(input_type):
-            raise ValueError(
-                f"Too many arguments for function {func.__name__}, "
-                f"expected {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 TypeError(
-                    f"Argument {arg} does NOT match the expected type "
-                    f"{expected_type} for parameter {param_name} in function {func.__name__}."
-                )
-            example_dict[param_name] = arg
-
-    else:
-        if len(kwargs) != len(input_type):
-            raise ValueError(
-                f"Mismatch in number of keyword arguments for function '{func.__name__}', "
-                f"expected {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 TypeError(
-                    f"Keyword argument {value} does NOT match the expected type "
-                    f"{expected_type} for parameter {key} in function {func.__name__}."
-                )
-            example_dict[key] = value
-
-    if hosta_out is None:
-        raise ValueError("Please provide hosta_out for output.")
-    else:
-        expected_output_type = output_type["hosta_out"]
-        if not isinstance(hosta_out, expected_output_type):
-            raise TypeError(
-                f"Output {hosta_out} does NOT match the expected type "
-                f"{expected_output_type} for function {func.__name__}."
-            )
-        example_dict["hosta_out"] = hosta_out
-
-
-def save_examples(hosta_func=None, hosta_path=None):
-    cached_data = {}
-
-    if hosta_func is None:
-        try:
-            func, _ = _extend_scope()
-        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, "a") as t:
-                    for dict in cached_data["ho_example"]:
-                        t.write(json.dumps(dict) + "\n")
-                        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_training_example(hosta_path: str, hosta_func: callable) -> dict:
-    """
-    Load the training example from the cache.
-    """
-    func_name = hosta_func.__name__
-    path_name = os.path.join(CACHE_DIR, f"{func_name}.openhc")
-
-    cached_data = None
-
-    if os.path.exists(path_name):
-        try:
-            with open(path_name, "rb") as f:
-                cached_data = pickle.load(f)
-        except (pickle.PickleError, IOError) as e:
-            raise ValueError(f"Error loading cache from {path_name}") from e
-    else:
-        cache = Hostacache(hosta_func, None)
-        cache.create_hosta_cache()
-        with open(path_name, "rb") as f:
-            cached_data = pickle.load(f)
-
-    _, file_extension = os.path.splitext(hosta_path)
-    if file_extension not in ['.json', '.jsonl', '.csv']:
-        raise ValueError("Unsupported file type. Please provide a JSON or JSONL or CSV file.")
-
-    try:
-        with open(hosta_path, 'r') as file:
-            if file_extension == '.json':
-                data = json.load(file)
-                if isinstance(data, list):
-                    for item in data:
-                        if item not in cached_data['ho_data']:
-                            cached_data['ho_data'].append(item)
-                else:
-                    if data not in cached_data['ho_data']:
-                        cached_data['ho_data'].append(data)
-            elif file_extension == '.jsonl':
-                for line in file:
-                    item = json.loads(line)
-                    if item not in cached_data['ho_data']:
-                        cached_data['ho_data'].append(item)
-            elif file_extension == '.csv':
-                reader = csv.DictReader(file)
-                for row in reader:
-                    if row not in cached_data['ho_data']:
-                        cached_data['ho_data'].append(row)
-        with open(path_name, "wb") as f:
-            pickle.dump(cached_data, f)
-    except (IOError, json.JSONDecodeError) as e:
-        raise ValueError(f"Error loading data from {hosta_path}") from e
-    return cached_data
-
-
-def _extend_scope() -> Callable:
-    func: Callable = None
-    current = None
-    step = None
-    caller = None
-
-    current = inspect.currentframe()
-    if current is None:
-        raise FrameError("Current frame is None")
-    step = current.f_back
-    if step is None:
-        raise FrameError("Caller[lvl1] frame is None")
-    caller = step.f_back
-    if caller is None:
-        raise FrameError("Caller[lvl2] frame is None")
-
-    caller_name = caller.f_code.co_name
-    caller_code = caller.f_code
-    l_caller = caller
-    
-    if "self" in caller.f_locals:
-        obj = caller.f_locals["self"]
-        func = getattr(obj, caller_name, None)
-        if func:
-            func = inspect.unwrap(func)
-    else:
-        while func is None and l_caller.f_back is not None:
-            for obj in l_caller.f_back.f_locals.values():
-                found = False
-                try:
-                    if hasattr(obj, "__code__"):
-                        found = True
-                except:
-                    continue       
-                if found and obj.__code__ == caller_code:
-                    func = obj
-                    break
-            if func is None:
-                l_caller = l_caller.f_back
-        if func is None:
-            func = caller.f_globals.get(caller_name)
-            if func:
-                func = inspect.unwrap(func)
-    
-    if func is None or not callable(func):
-        raise FrameError("The emulated function cannot be found.")
-
-    return func, caller
-
-
-EXAMPLE_DOC = """
-A utility function that performs runtime type validation on a given function's arguments and output.
-
-Parameters:
-    *args: 
-        Positional arguments to validate against the input types of the provided function (hosta_func).
-    **kwargs: 
-        Keyword arguments (passed by name) to validate against the input types of the provided function.
-    hosta_func (function, optional but recommended): 
-        The function whose signature will be used for input/output type validation.
-    hosta_out (object): 
-        The expected output of hosta_func, to be validated against the return type annotation.
-
-Raises:
-    ValueError: 
-        If the number of arguments provided does not match the expected number as per the function's signature.
-    TypeError: 
-        If the type of any argument or output does not match the expected type.
-"""
\ No newline at end of file
diff --git a/DEPRECATED/body/thought.py b/DEPRECATED/body/thought.py
deleted file mode 100644
index efdf0c2..0000000
--- a/DEPRECATED/body/thought.py
+++ /dev/null
@@ -1,85 +0,0 @@
-import sys
-import json
-from pydoc import locate
-from pydantic import create_model
-
-from ..core.emulate import _exec_emulate
-from ..core.config import DefaultManager
-from ..utils.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
-
-def tmp():
-    x = thought("add two")
-    x(4)
-    x(4)
-    x(7)
-    x = thought("Salut je suis un fermier ! Raconte moi une histoire")
-    y = thought("add two")
- 
- 
\ No newline at end of file
diff --git a/DEPRECATED/core/__init__.py b/DEPRECATED/core/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/DEPRECATED/core/config.py b/DEPRECATED/core/config.py
deleted file mode 100644
index 9afcd37..0000000
--- a/DEPRECATED/core/config.py
+++ /dev/null
@@ -1,199 +0,0 @@
-import sys
-import requests
-import json
-import re
-from jsonschema import validate
-from pydantic import BaseModel
-from typing import get_origin, get_args
-
-from ..utils.errors import ApiKeyError
-
-
-def is_valid_url(url):
-    regex = re.compile(
-        r"^(?:http|ftp)s?://"
-        r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|"
-        r"localhost|"
-        r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|"
-        r"\[?[A-F0-9]*:[A-F0-9:]+\]?)"
-        r"(?::\d+)?"
-        r"(?:/?|[/?]\S+)$",
-        re.IGNORECASE,
-    )
-    return re.match(regex, url) is not None
-
-
-class Model:
-
-    _SYS_PROMPT = ""
-
-    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._last_request = None
-        
-        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),
-            type(None): lambda x: None,
-        }
-
-        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.")
-            return
-
-    def __str__(self) -> str:
-        return (
-            f"[OpenHosta] <{self.__class__.__module__}.{self.__class__.__name__} object at {hex(id(self))}>\n"
-            f"Model: {self.model}\n"
-            f"Base_url: {self.base_url}\n"
-            "Infos:\n"
-        )
-
-    def api_call(
-        self, sys_prompt: str, user_prompt: str, creativity: float, diversity: float
-    )->requests.models.Response:
-        if self.api_key is None or not self.api_key:
-            raise ApiKeyError("Empty API key.")
-
-        l_body = {
-            "model": self.model,
-            "messages": [
-                {
-                    "role": "system",
-                    "content": [{"type": "text", "text": sys_prompt}],
-                },
-                {
-                    "role": "user",
-                    "content": [{"type": "text", "text": user_prompt}],
-                },
-            ],
-            "response_format": {"type": "json_object"},
-            "temperature": creativity if creativity is not None else 0.7,
-            "top_p": diversity if diversity is not None else 0.7,
-        }
-        headers = {
-            "Content-Type": "application/json",
-            "Authorization": f"Bearer {self.api_key}",
-        }
-        self._last_request = l_body        
-
-        try:
-            response = requests.post(self.base_url, json=l_body, headers=headers)
-            response.raise_for_status()
-        except requests.exceptions.RequestException as e:
-            sys.stderr.write(f"\n[CALL_ERROR] Request failed:\n{e}\n\n")
-        if response.status_code != 200:
-            if "invalid_api_key" in str(response.text):
-                raise ApiKeyError("Incorrect API key.")
-            else:    
-                sys.stderr.write(f"[CALL_ERROR] API call was unsuccessful.\nStatus code: {response.status_code}:\n{response.text}")
-        return response
-
-    def request_handler(self, response, return_type, return_caller):
-        l_ret = None
-
-        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", {}),
-            # )                                                 # REFACTO
-        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]
-                if l_ret_data["return"] is not None:
-                    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)
-
-        elif "return_hosta_type_any" in return_type["properties"]:
-            l_ret = l_ret_data["return"]
-
-        elif issubclass(return_caller, BaseModel):
-            try:
-                l_ret = return_caller(**l_ret_data["return"])
-            except:
-                sys.stderr.write("Unable t parse answer: ", l_ret_data["return"])
-                for m in self.__last_request["messages"]:
-                    sys.stderr.write(" "+m["role"]+">\n=======\n", m["content"][0]["text"])
-                sys.stderr.write("Answer>\n=======\n",  l_ret_data["return"])
-                l_ret = None
-
-        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)
-
-        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
-
-
-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 __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")
-
-    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)
diff --git a/DEPRECATED/core/emulate.py b/DEPRECATED/core/emulate.py
deleted file mode 100644
index 597550e..0000000
--- a/DEPRECATED/core/emulate.py
+++ /dev/null
@@ -1,85 +0,0 @@
-import sys
-import inspect
-
-from ..utils.prompt import PromptMananger
-from .config import Model, DefaultManager
-
-
-_x = PromptMananger()
-
-_emulator_pre_prompt = _x.get_prompt("emulate")
-
-l_default = DefaultManager.get_default_model()
-
-
-def build_user_prompt(_infos: dict = None):
-    filler = lambda pre, value: f"**{pre}**\n{str(value)}\n\n" if value is not None and value != [] else ""
-    
-    user_prompt = (
-        "---\n\n## Function infos\n\n"
-        + filler("Here's the function definition:", _infos["function_def"])
-        + filler("Here's the function's locals variables which you can use as additional information to give your answer:", _infos["function_locals"])
-        + "To fill in the \"return\" value in the output JSON, create your response according to the specified JSON Schema. Make sure not to change the key \"return.\"\n\n"
-        + filler("JSON Schema to be used for \"return\" structure", _infos["return_type"])
-        + filler("Here are some examples of expected input and output:", _infos["ho_example"])
-        + "---\n"
-    )
-    
-    return user_prompt
-
-
-def _exec_emulate(
-    _infos: dict = None,
-    _obj: object = None,
-    model: Model = None,
-    l_creativity: float = None,
-    l_diversity: float = None,
-):
-    global _emulator_pre_prompt
-
-    _function_return_caller = _infos["return_caller"]
-    _function_return = _infos["return_type"]
-    
-    if model is None:
-        model = DefaultManager.get_default_model() # doublons
-
-    try:
-        if not isinstance(_emulator_pre_prompt, str) or not _emulator_pre_prompt:
-            raise ValueError("Invalid prompt.")
-        if (l_creativity is not None and (l_creativity < 0 or l_creativity > 1)) or (
-            l_diversity is not None and (l_diversity < 0 or l_diversity > 1)
-        ):
-            raise ValueError("Emulate out of range values (0<creativity|diversity<1)")
-    except ValueError as v:
-        sys.stderr.write(f"[EMULATE_ERROR]: {v}")
-        return None
-
-    function_infos = build_user_prompt(_infos)
-
-    response = model.api_call(
-        sys_prompt=f"{_emulator_pre_prompt}\n{function_infos}\n",
-        user_prompt=_infos["function_call"],
-        creativity=l_creativity,
-        diversity=l_diversity,
-    )
-    
-    if _obj is not None and inspect.isfunction(_obj):
-        setattr(_obj, "_last_response", response.json())
-        
-    if response.status_code == 200:
-        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
-        
-    if _obj is not None and inspect.isfunction(_obj):
-        setattr(
-            _obj, 
-            "_last_request", 
-            f"{_emulator_pre_prompt}\n{function_infos}\n{_infos['function_call']}"
-        )
-
-    return l_ret
diff --git a/DEPRECATED/core/exec.py b/DEPRECATED/core/exec.py
deleted file mode 100644
index 2a0f8a3..0000000
--- a/DEPRECATED/core/exec.py
+++ /dev/null
@@ -1,272 +0,0 @@
-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
-import copy
-
-import functools
-
-from ..body.enhancer import enhance
-from ..utils.errors import FrameError
-from ..predict.predict import continue_train, to_emulate, retrain
-
-
-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):
-        self.infos_cache = {
-            "hash_function": "",
-            "function_def": "",
-            "return_type": "",
-            "return_caller": "",
-            "function_call": "",
-            "function_args": {},
-            "function_locals": {},
-            "ho_example": [],
-            "ho_example_id": 0,
-            "ho_example_links": [],
-            "ho_cothougt": [],
-            "ho_cothougt_id": 0,
-            "ho_data": [],
-            "ho_data_id" : 0
-        }
-        func_obj, caller = self._extend_scope()
-        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)
-            if function_hash == cached_data["hash_function"]:
-                cached_data["function_call"], cached_data["function_locals"], cached_data["function_args"] = (
-                    self._get_functionCall(func_obj, caller)
-                )
-                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"], hosta_args["function_args"] = (
-            self._get_functionCall(func_obj, caller)
-        )
-        self._attach_attributs(func_obj, hosta_args["function_def"])
-        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"],
-        )
-        return self.infos_cache
-
-    def _extend_scope(self) -> Callable:
-        func: Callable = None
-        current = None
-        step = None
-        caller = None
-
-        current = inspect.currentframe()
-        if current is None:
-            raise FrameError("Current frame is None")
-        step = current.f_back
-        if step is None:
-            raise FrameError("Caller[lvl1] frame is None")
-        caller = step.f_back
-        if caller is None:
-            raise FrameError("Caller[lvl2] frame is None")
-
-        caller_name = caller.f_code.co_name
-        caller_code = caller.f_code
-        l_caller = caller
-        
-        if "self" in caller.f_locals:
-            obj = caller.f_locals["self"]
-            func = getattr(obj, caller_name, None)
-            if func:
-                func = inspect.unwrap(func)
-        else:
-            while func is None and l_caller.f_back is not None:
-                for obj in l_caller.f_back.f_locals.values():
-                    found = False
-                    try:
-                        if hasattr(obj, "__code__"):
-                            found = True
-                    except:
-                        continue       
-                    if found and obj.__code__ == caller_code:
-                        func = obj
-                        break
-                if func is None:
-                    l_caller = l_caller.f_back
-            if func is None:
-                func = caller.f_globals.get(caller_name)
-                if func:
-                    func = inspect.unwrap(func)
-        
-        if func is None or not callable(func):
-            raise FrameError("The emulated function cannot be found.")
-
-        return func, caller
-
-    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"```python\ndef {func_name}({func_params}):{func_return}\n"
-            f"    \"\"\"\n\t{func.__doc__}\n    \"\"\"\n```"
-        )
-        prototype = f"def {func_name}({func_params}):{func_return}"
-        return definition, prototype
-
-    def _get_functionCall(self, func: Callable, caller) -> str:
-        locals = None
-        _, _, _, values = inspect.getargvalues(caller)
-
-        sig = inspect.signature(func)
-
-        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(
-            f"{name}={value!r}" if name in bound_args.kwargs else f"{value!r}"
-            for name, value in bound_args.arguments.items()
-        )
-
-        call = f"{func.__name__}({args_str})"
-        return call, locals, values_args
-
-    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.Annotated,
-            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
-
-    def _attach_attributs(self, func: Callable, prototype: str)->None:
-        """
-        Attach additional attributes to a function.
-
-        Args:
-            func (Callable): The target function to which the attributes are attached.
-            prototype (str): A string representing the prototype (used as an example).
-
-        Returns:
-            Callable: The target function wrapped with the attached attributes.
-        """
-        if "bound method" not in str(func):
-            setattr(func, "__suggest__", enhance)
-            setattr(func, "_prot", prototype) 
-            setattr(func, "continue_train", functools.partial(continue_train, func_obj=func))
-            setattr(func, "retrain", functools.partial(retrain, func_obj=func))
-            setattr(func, "emulate", functools.partial(to_emulate, func_obj=func))
-
diff --git a/DEPRECATED/predict/__init__.py b/DEPRECATED/predict/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/DEPRECATED/predict/build.py b/DEPRECATED/predict/build.py
deleted file mode 100644
index affee02..0000000
--- a/DEPRECATED/predict/build.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from ..core.config import Model, DefaultManager
-
-def _exec_build(
-        _function_infos : dict = None,
-        _function_obj: object = None,
-        model: Model = None,
-
-):
-    pass
-    
diff --git a/DEPRECATED/predict/builder.py b/DEPRECATED/predict/builder.py
deleted file mode 100644
index 304e9dd..0000000
--- a/DEPRECATED/predict/builder.py
+++ /dev/null
@@ -1,57 +0,0 @@
-import json
-import os
-import torch
-
-from .model import CustomLinearModel
-
-class Builder():
-    def __init__(self, hidden_dir):
-
-        self.hidden_dir = hidden_dir
-
-
-    def build(self, len_input, len_output, complexity, config, optimizer, loss):
-        assert len_input > 0, "Input size must be greater than 0"
-        assert len_output > 0, "Output size must be greater than 0"
-
-        if complexity == None:
-            complexity = 5
-        if optimizer != None:
-            print("\033[93mWarning: The change of optimizer is not available for now, AdamW is actually used.\033[0m")
-            optimizer = "AdamW"
-        if loss != None:
-            print("\033[93mWarning: The change of loss are not available for now, Smooth1Loss is actually used.\033[0m")
-            loss = "SmoothL1Loss"
-
-        if config == None:
-            config = {
-                "architecture": "LinearRegression",
-                "input_size": len_input,
-                "hidden_size_1": len_input * (2 * complexity),
-                "hidden_size_2": len_input * (4 * complexity),
-                "hidden_size_3": len_input * (2 * complexity),
-                "output_size": len_output,
-                "optimizer": optimizer,
-                "loss": loss
-            }
-
-        config_json = json.dumps(config)        
-        config_path = os.path.join(self.hidden_dir, "config.json")
-        
-        with open(config_path, "w") as f:
-            f.write(config_json)
-        return config["architecture"]
-
-    def load_inference(self, config_path, weight_path, inference):
-        with open(config_path, "r") as file:
-            config = json.load(file)
-  
-        model = CustomLinearModel(config, self.hidden_dir)
-        model.load_state_dict(torch.load(weight_path, weights_only=True))
-        output = model.forward(inference)
-        return output
-
-    def trains(self, config, train, val, epochs, verbose, get_loss, continue_training):
-        
-        model = CustomLinearModel(config, self.hidden_dir)
-        model.train(train, val, epochs, self.hidden_dir, verbose, get_loss, continue_training)
diff --git a/DEPRECATED/predict/datapreparator.py b/DEPRECATED/predict/datapreparator.py
deleted file mode 100644
index ad21f6d..0000000
--- a/DEPRECATED/predict/datapreparator.py
+++ /dev/null
@@ -1,241 +0,0 @@
-import torch
-from torch.utils.data import DataLoader
-import os
-import json
-import csv
-import numpy as np
-
-from .encoder import HostaEncoder
-from .decoder import HostaDecoder
-
-class Datapreparator():
-    def __init__(self, norm_max, norm_min, encoder=None, decoder=None):
-        self.encoder = encoder if encoder else HostaEncoder()
-        self.decoder = decoder if decoder else HostaDecoder()
-
-        if norm_min:
-            self.norm_min = norm_min
-        else:
-            self.norm_min = 0.1
-        if norm_max:
-            self.norm_max = norm_max
-        else:
-            self.norm_max = 1.0
-    
-        self.data_min_nonzero = None
-        self.data_max = None
-        self.data_min = None
-        self.data_range = None
-
-        self.prediction_min_nonzero = None
-        self.prediction_max = None
-        self.prediction_min = None
-        self.prediction_range = None
-
-    def prepare_input(self, in_value):
-        input_data = []
-        for key, value in in_value.items():
-            if isinstance(value, dict):
-                for sub_key, sub_value in value.items():
-                    parsed_value = self.encoder.encode(sub_value)
-                    input_data.extend(parsed_value)
-            elif isinstance(value, list):
-                for item in value:
-                    parsed_value = self.encoder.encode(item)
-                    input_data.extend(parsed_value)
-            else:
-                parsed_value = self.encoder.encode(value)
-                input_data.extend(parsed_value)
-        return input_data
-
-    def prepare(self, function_infos, prediction):
-        train = []
-        val = []
-        if function_infos["ho_example"] == [] and function_infos["ho_data"] == []:
-            raise ValueError("No example provided please provide at least one example for the model")
-
-        if function_infos["ho_data"] != []:
-            for example in function_infos["ho_data"]:
-                value = self.parse_dict(example, prediction)
-                train.extend(value)
-            if function_infos["ho_example"] != []:
-                for example in function_infos["ho_example"]:
-                    value = self.parse_dict(example, prediction)
-                    val.extend(value)
-        else:
-            for example in function_infos["ho_example"]:
-                value = self.parse_dict(example, prediction)
-                train.extend(value)
-        return train, val
-
-    def normalize_dataset(self, train, val):
-        dataset = train + val if val != [] else train
-        data_values = [example[0] for example in dataset]
-        prediction_values = [example[1] for example in dataset]
-
-        data_array = np.array(data_values)
-        prediction_array = np.array(prediction_values)
-
-        negative_data = np.any(data_array < 0, axis=0)
-        negative_prediction = np.any(prediction_array < 0, axis=0)
-
-        self.data_min_nonzero = np.array([
-            np.min(data_array[:, i][data_array[:, i] > 0]) if not negative_data[i] and np.any(data_array[:, i] > 0) else 0
-            for i in range(data_array.shape[1])])
-        self.data_max = data_array.max(axis=0)
-        self.data_min = data_array.min(axis=0)
-
-        self.prediction_min_nonzero = np.array([
-            np.min(prediction_array[:, i][prediction_array[:, i] > 0]) if not negative_prediction[i] and np.any(prediction_array[:, i] > 0) else 0
-            for i in range(prediction_array.shape[1])])
-        self.prediction_max = prediction_array.max(axis=0)
-        self.prediction_min = prediction_array.min(axis=0)
-
-        self.data_range = self.data_max - self.data_min_nonzero
-        self.data_range[self.data_range == 0] = 1 
-
-        self.prediction_range = self.prediction_max - self.prediction_min_nonzero
-        self.prediction_range[self.prediction_range == 0] = 1
-
-        normalized_data = np.zeros_like(data_array)
-        for i in range(data_array.shape[1]):
-            zero_mask = data_array[:, i] == 0
-            normalized_data[:, i] = np.where(zero_mask, 0.0, self.norm_min + ((data_array[:, i] - self.data_min_nonzero[i]) / self.data_range[i]) * (self.norm_max - self.norm_min))
-
-        normalized_prediction = np.zeros_like(prediction_array)
-        for i in range(prediction_array.shape[1]):
-            zero_mask = prediction_array[:, i] == 0
-            normalized_prediction[:, i] = np.where(zero_mask, 0.0, self.norm_min + ((prediction_array[:, i] - self.prediction_min_nonzero[i]) / self.prediction_range[i]) * (self.norm_max - self.norm_min))
-
-        # Maybe unwrap the tolist, stay for now because only work after with list 
-        normalized_dataset = list(zip(normalized_data.tolist(), normalized_prediction.tolist()))
-        train = normalized_dataset[:len(train)]
-        val = normalized_dataset[len(train):] if val else None
-        return train, val
-
-    def normalize_inference(self, inference_data):
-        inference_data = np.array(inference_data)
-
-        normalized_inference = np.zeros_like(inference_data)
-        for i in range(len(inference_data)):
-            if inference_data[i] == 0:
-                normalized_inference[i] = 0.0
-            else:
-                normalized_inference[i] = self.norm_min + ((inference_data[i] - self.data_min_nonzero[i]) / self.data_range[i]) * (self.norm_max - self.norm_min)
-            
-        return normalized_inference.tolist()
-    
-    def denormalize_prediction(self, prediction):
-        prediction = prediction.detach().cpu().numpy()
-
-        denormalized_prediction = np.zeros_like(prediction)
-        for i in range(len(prediction)):
-            if prediction[i] == 0:
-                denormalized_prediction[i] = 0.0
-            else:
-                denormalized_prediction[i] = self.prediction_min_nonzero[i] + ((prediction[i] - self.norm_min) / (self.norm_max - self.norm_min)) * self.prediction_range[i]
-    
-        return denormalized_prediction.tolist()
-
-    def save_normalization_params(self, path):
-        params = {
-            'norm_min': self.norm_min,
-            'norm_max': self.norm_max,
-            'data_min_nonzero': self.data_min_nonzero.tolist(),
-            'data_max': self.data_max.tolist(),
-            'data_min': self.data_min.tolist(),
-            'data_range': self.data_range.tolist(),
-            'prediction_min_nonzero': self.prediction_min_nonzero.tolist(),
-            'prediction_max': self.prediction_max.tolist(),
-            'prediction_min': self.prediction_min.tolist(),
-            'prediction_range': self.prediction_range.tolist()
-        }
-        with open(path, 'w') as f:
-            json.dump(params, f)
-
-    def load_normalization_params(self, path):
-        try:
-            with open(path, 'r') as f:
-                params = json.load(f)
-                self.norm_min = params['norm_min']
-                self.norm_max = params['norm_max']
-                self.data_min_nonzero = np.array(params['data_min_nonzero'])
-                self.data_max = np.array(params['data_max'])
-                self.data_min = np.array(params['data_min'])
-                self.data_range = np.array(params['data_range'])
-                self.prediction_min_nonzero = np.array(params['prediction_min_nonzero'])
-                self.prediction_max = np.array(params['prediction_max'])
-                self.prediction_min = np.array(params['prediction_min'])
-                self.prediction_range = np.array(params['prediction_range'])
-        except Exception as e:
-            raise IOError(f"An error occurred while loading the normalization parameters: {e}")
-
-    def convert(self, inference):
-        return torch.tensor(inference, dtype=torch.float32)
-    
-    def split(self, train_normalization, val_normalization, batch_size):
-        datatensor = []
-
-        for examples in train_normalization:
-            feature_tensor = torch.tensor(examples[0], dtype=torch.float32)
-            label_tensor = torch.tensor(examples[1], dtype=torch.float32)
-
-            tensor = [feature_tensor, label_tensor]
-            datatensor.append(tensor)
-
-        train = DataLoader(datatensor, batch_size=batch_size, shuffle=True)
-
-        if val_normalization:
-            valtensor = []
-            for examples in val_normalization:
-                feature_tensor = torch.tensor(examples[0], dtype=torch.float32)
-                label_tensor = torch.tensor(examples[1], dtype=torch.float32)
-
-                tensor = [feature_tensor, label_tensor]
-                valtensor.append(tensor)
-            val = DataLoader(valtensor, batch_size=batch_size, shuffle=False)
-        else : val = None
-        return train, val
-
-    def parse_dict(self, example, prediction):
-        dataset = []
-        input_data = []
-        output_data = []
-        for key, value in example.items():
-            if key in prediction or key == "hosta_out":
-                parsed_value = self.encoder.encode(value)
-                output_data.extend(parsed_value)
-            else:
-                parsed_value = self.encoder.encode(value)
-                input_data.extend(parsed_value)
-        dataset.append([input_data, output_data])
-        return dataset
-
-def open_file(ho_examples):
-    list_of_examples = []
-    for path in ho_examples:
-        _, file_extension = os.path.splitext(path)
-        try:
-            if file_extension == '.jsonl':
-                with open(path, "r") as file:
-                    for line in file:
-                        example = json.loads(line.strip())
-                        list_of_examples.append(example)
-
-            elif file_extension == '.csv':
-                with open(path, "r", newline='') as file:
-                    csv_reader = csv.DictReader(file)
-                    for row in csv_reader:
-                        list_of_examples.append(row)
-
-            elif file_extension == '.txt':
-                with open(path, "r") as file:
-                    for line in file:
-                        list_of_examples.append(line.strip())
-            
-            else:
-                raise ValueError("Unsupported file type. Please provide a JSONL, CSV, or TXT file.")
-
-        except Exception as e:
-            raise IOError(f"An error occurred while processing the file: {e}")
-    return list_of_examples
diff --git a/DEPRECATED/predict/decoder.py b/DEPRECATED/predict/decoder.py
deleted file mode 100644
index c76aadb..0000000
--- a/DEPRECATED/predict/decoder.py
+++ /dev/null
@@ -1,3 +0,0 @@
-class HostaDecoder():
-    def __init__(self) -> None:
-        pass
diff --git a/DEPRECATED/predict/encoder.py b/DEPRECATED/predict/encoder.py
deleted file mode 100644
index a975ad0..0000000
--- a/DEPRECATED/predict/encoder.py
+++ /dev/null
@@ -1,36 +0,0 @@
-from typing import Any
-
-class HostaEncoder():
-    def __init__(self) -> None:
-        None
-
-    def encode(self, value: Any):
-        if type(value) == int:
-            return IntEncoder.encoder(value)
-        elif type(value) == float:
-            return FloatEncoder.encoder(value)
-        elif type(value) == str:
-            try:
-                convert_type = FloatEncoder.encoder(value)
-                return convert_type
-            except:
-                raise ValueError("String cannot be converted to float (numbers in string only supported for now)")
-        else:
-            raise ValueError("Type not supported")
-
-
-class IntEncoder(HostaEncoder):
-    def __init__(self) -> None:
-        super().__init__()
-
-    def encoder(data):
-        data_encode = int(data)
-        return [data_encode]
-
-class FloatEncoder(HostaEncoder):
-    def __init__(self) -> None:
-        super().__init__()
-
-    def encoder(data):
-        data_encode = float(data)
-        return [data_encode]
diff --git a/DEPRECATED/predict/predict.py b/DEPRECATED/predict/predict.py
deleted file mode 100644
index d3e92a8..0000000
--- a/DEPRECATED/predict/predict.py
+++ /dev/null
@@ -1,195 +0,0 @@
-import os
-
-import pickle
-from ..utils.cache import Hostacache
-from .builder import Builder
-from .datapreparator import Datapreparator
-from ..body.example import type_verificator
-from ..core.emulate import _exec_emulate
-
-from typing import Any
-import inspect
-
-CACHE_DIR = "__hostacache__"
-os.makedirs(CACHE_DIR, exist_ok=True)
-
-def _exec_predict(
-        _function_infos: dict = None,
-        _function_obj: object = None,
-
-        encoder = None,
-        decoder = None,
-        verbose: bool = False,
-        prediction: list = [],
-        complexity: int = None,
-        config: dict = None,
-        optimizer: str = None,
-        loss: str = None,
-        epochs: int = None,
-        get_loss: float = 0.0,
-        batch_size: int = None,
-        force_train: bool = False,
-        norm_max: float = None,
-        norm_min: float = None,
-        continue_training: bool = False,
-        normalization: bool = False
-):
-    hidden_dir = os.path.join(CACHE_DIR, f".model_{_function_obj.__name__}_{_function_infos['hash_function']}")
-    os.makedirs(hidden_dir, exist_ok=True)
-
-    config_path = os.path.join(hidden_dir, "config.json")
-    weight_path = os.path.join(hidden_dir, "model.pth")
-    normalisation_path = os.path.join(hidden_dir, "normalisation.json")
-
-    preparator = Datapreparator(norm_max, norm_min, encoder, decoder)
-    builder = Builder(hidden_dir)
-
-    if not os.path.exists(config_path) or not os.path.exists(weight_path) or force_train==True:
-
-        train, val = preparator.prepare(_function_infos, prediction)
- 
-        if normalization:  
-            train, val = preparator.normalize_dataset(train,val)
-            preparator.save_normalization_params(normalisation_path)
-        len_input = len(train[0][0])
-        len_output = len(train[0][1])
-        builder.build(len_input, len_output, complexity, config, optimizer, loss)
-        if batch_size is None:
-            batch_size = int(0.05 * len(train)) if 0.05 * len(train) > 1 else len(train) # 5% of the dataset or len(train) if len(train)
-        else:
-            batch_size = batch_size
-        save_len = len(train)
-        train, eval = preparator.split(train, val, batch_size)
-        epochs = int(2*save_len / batch_size if batch_size != save_len else 2*save_len) if epochs is None else epochs
-        assert epochs > 0, "epochs must be greater than 0 now it's {epochs}"
-        builder.trains(config, train, eval, epochs=epochs, verbose=verbose, get_loss=get_loss, continue_training=continue_training)
-    else:
-        if verbose:
-            print("\033[93mModel already trained, skipping training\033[0m")
-        if normalization:
-            preparator.load_normalization_params(normalisation_path)
-    if _function_infos["function_args"] != {}:
-        inference = preparator.prepare_input(_function_infos["function_args"])
-        if normalization:
-            inference = preparator.normalize_inference(inference)
-        torch_inference = preparator.convert(inference)
-
-        prediction = builder.load_inference(config_path, weight_path, torch_inference)
-        if normalization:
-            prediction_denormalize = preparator.denormalize_prediction(prediction)
-            result = float(prediction_denormalize[0])
-        else:
-            result = float(prediction.detach().cpu().numpy()[0])
-        return result
-
-
-def continue_train(func_obj, epochs=None, get_loss=None, verbose=False):
-    """
-    Continue the training of the model
-    - Reload a pth and add a dataset or not for the model
-    save a new pth after the training decided in the emulate or not or in this function also (diff parameters
-    of training and not architecture)
-    """
-    infos_cache = load_cache(func_obj)
-    return _exec_predict(_function_infos=infos_cache, _function_obj=func_obj, force_train=True ,continue_training=True, epochs=epochs, get_loss=get_loss, verbose=verbose)
-
-
-def get_input_types_from_signature(func_obj):
-    """
-    Extract input type from function signature
-    """
-    signature = inspect.signature(func_obj)
-    input_type = {}
-    for name, param in signature.parameters.items():
-        if param.annotation != inspect.Parameter.empty:
-            input_type[name] = param.annotation
-        else:
-            input_type[name] = Any
-    return input_type
-
-
-def emulate_verificator(args, kwargs, input_type, func, example_dict):
-    """
-    Vérifie les types des arguments positionnels et nommés lors de l'appel à emulate.
-    Met à jour example_dict avec les valeurs validées.
-    """
-    param_names = list(input_type.keys())
-    
-    total_args_provided = len(args) + len(kwargs)
-    total_args_expected = len(param_names)
-    
-    if total_args_provided != total_args_expected:
-        raise ValueError(
-            f"Incorrect number of arguments for function '{func.__name__}', "
-            f"expected {total_args_expected}, got {total_args_provided}."
-        )
-    
-    for i, arg in enumerate(args):
-        param_name = param_names[i]
-        expected_type = input_type[param_name]
-        
-        if not isinstance(arg, expected_type):
-            raise TypeError(
-                f"Positional argument '{param_name}'={arg} does not match the expected type "
-                f"{expected_type} in function '{func.__name__}'."
-            )
-        example_dict[param_name] = arg
-    
-    for key, value in kwargs.items():
-        if key not in input_type:
-            raise ValueError(
-                f"Unexpected named argument '{key}' for function '{func.__name__}'."
-            )
-        expected_type = input_type[key]
-        
-        if not isinstance(value, expected_type):
-            raise TypeError(
-                f"Named argument '{key}'={value} does not match the expected type "
-                f"{expected_type} in function '{func.__name__}'."
-            )
-        example_dict[key] = value
-
-
-def to_emulate(*args, func_obj, model=None, l_creativity=None, l_diversity=None, **kwargs):
-    """
-    Emulate the function with the given arguments and keyword arguments.
-    """
-    infos_cache = load_cache(func_obj)
-    input_type = get_input_types_from_signature(func_obj)
-
-    example_dict = {}
-
-    emulate_verificator(args=args, kwargs=kwargs, input_type=input_type, func=func_obj, example_dict=example_dict)
-    infos_cache["function_args"] = example_dict
-    infos_cache["function_call"] = f"{func_obj.__name__}({', '.join([f'{k}={v}' for k, v in example_dict.items()])})"
-    return _exec_emulate(_infos=infos_cache, _obj=func_obj, model=model, l_creativity=l_creativity, l_diversity=l_diversity)
-
-
-def load_cache(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)
-        return cached_data
-    else:
-        raise ValueError(f"Cache not found for function '{func_name}'.")
-
-
-def retrain(func_obj=None, force_train=True, epochs=None, get_loss=None, verbose=False):
-
-    infos_cache = load_cache(func_obj)
-    return _exec_predict(_function_infos=infos_cache, _function_obj=func_obj, force_train=force_train, epochs=epochs, get_loss=get_loss, verbose=verbose)
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/DEPRECATED/predict/trainset.py b/DEPRECATED/predict/trainset.py
deleted file mode 100644
index e58b248..0000000
--- a/DEPRECATED/predict/trainset.py
+++ /dev/null
@@ -1,59 +0,0 @@
-import inspect
-
-from ..utils.cache import Hostacache
-from ..body.example import type_verificator
-from ..core.config import DefaultManager
-from .predict import load_cache
-
-l_default = DefaultManager.get_default_model()
-
-class TrainingSet():
-    def __init__(self, func : callable):
-        assert callable(func), "Please provide an hosta-injected function"
-        self.func = func
-        self.infos_cache = None
-
-    def visualize(self):
-        """
-        function for visualize the training set idk how for now
-        maybe let the hostashpère do it
-        """
-        hosta_cache = load_cache(self.func)
-        print("ho_example:")
-        for i in range(len(hosta_cache["ho_example"])):
-            print(hosta_cache["ho_example"][i])
-        print("ho_data:")
-        for i in range(len(hosta_cache["ho_data"])):
-            print(hosta_cache["ho_data"][i])
-        return [hosta_cache["ho_example"], hosta_cache["ho_data"]]
-
-    def add(self, *args, hosta_out=None,**kwargs):
-        """
-        function for add an example to the training set
-        """
-        input_type = {}
-        output_type = {}
-        data_dict = {}
-        hosta_func = self.func
-
-        if hosta_out is None:
-            raise ValueError("Please provide hosta_out for output.")
-        if hosta_func is None:
-                raise ValueError("Please provide hosta_func for specifying the function")
-        elif callable(hosta_func):
-            func = hosta_func
-        else:
-            raise ValueError("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("Function does not have a signature")
-
-        type_verificator(args, kwargs, input_type, output_type, hosta_out, func, data_dict)
-        cache_id = "ho_data"
-        cache = Hostacache(func, cache_id, data_dict)
-        cache.create_hosta_cache()
diff --git a/DEPRECATED/py.typed b/DEPRECATED/py.typed
deleted file mode 100644
index e69de29..0000000
diff --git a/DEPRECATED/utils/__init__.py b/DEPRECATED/utils/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/DEPRECATED/utils/analytics.py b/DEPRECATED/utils/analytics.py
deleted file mode 100644
index a6efb53..0000000
--- a/DEPRECATED/utils/analytics.py
+++ /dev/null
@@ -1,127 +0,0 @@
-import tiktoken
-from enum import Enum
-import time as t
-import sys
-import json
-
-from ..core.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):
-
-    _default_input_cost: int = 0.005
-    _default_output_cost: int = 0.015
-    _default_token_perSec = 63.32
-    _default_latency = 0.48
-
-    def __init__(
-        self,
-        name: str,
-        input_cost: float,
-        output_cost: float,
-        latency: float,
-        token_perSec: float,
-    ):
-        self.name = self._default_name if name is None else name
-        self.input_cost = self._default_input_cost if input_cost is None else input_cost
-        self.output_cost = (
-            self._default_output_cost if output_cost is None else output_cost
-        )
-        self.latency = self._default_latency if latency is None else latency
-        self.token_perSec = (
-            self._default_token_perSec if token_perSec is None else token_perSec
-        )
-        self.tokenizer = tiktoken.get_encoding("cl100k_base")
-
-    def get_input_cost(self):
-        return self.input_cost
-
-    def get_output_cost(self):
-        return self.output_cost
-
-    def get_latency(self):
-        return self.latency
-
-    def get_token_perSec(self):
-        return self.token_perSec
-
-    def _estimate_output_token(self, function_doc: str, function_call: str):
-        global _estimate_prompt, l_default
-
-        try:
-            if not _estimate_prompt:
-                raise ValueError("ValueError -> emulate empty values")
-        except ValueError as v:
-            sys.stderr.write(f"[ESTIMATE_ERROR]: {v}")
-            return None
-
-        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()
-            json_string = data["choices"][0]["message"]["content"]
-            try:
-                l_ret_data = json.loads(json_string)
-
-            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["tokens"]
-        else:
-            sys.stderr.write(f"Error {response.status_code}: {response.text}")
-            l_ret = None
-
-        return l_ret
-
-    def _compute_request_cost(self, input_text, output_token):
-        input_tokens = self.tokenizer.encode(input_text)
-        num_input_tokens = len(input_tokens)
-        num_output_tokens = output_token
-        cost_input = (num_input_tokens / 1000) * self.input_cost
-        cost_output = (num_output_tokens / 1000) * self.output_cost
-        total_cost = cost_input + cost_output
-        return total_cost
-
-    def _compute_request_duration(self, output_token):
-        total = self.latency
-        total += self.token_perSec / output_token
-        total += 0.5  # Processing duration margin
-        return total
-
-
-def request_timer(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
-
-    return wrapper
diff --git a/DEPRECATED/utils/cache.py b/DEPRECATED/utils/cache.py
deleted file mode 100644
index 35f2ede..0000000
--- a/DEPRECATED/utils/cache.py
+++ /dev/null
@@ -1,199 +0,0 @@
-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
-
-
-
-
-
-class Hostacache:
-    def __init__(self, func, cache_id=None, value=None) -> 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_args": {},
-            "function_locals": {},
-            "ho_example": [],
-            "ho_example_id": 0,
-            "ho_example_links": [],
-            "ho_cothougt": [],
-            "ho_cothougt_id": 0,
-            "ho_data": [],
-            "ho_data_id": 0,
-        }
-
-    def create_hosta_cache(self):
-        func_name = self.func.__name__
-        path_name = os.path.join(CACHE_DIR, f"{func_name}.openhc")
-
-        if self.cache_id is None:
-            if os.path.exists(path_name):
-                with open(path_name, "rb") as f:
-                    cached_data = pickle.load(f)
-                    return cached_data
-            else:
-                return self._parse_and_create_cache_file(path_name)
-
-        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.value is not None:
-                if not self._is_value_already_in_example(self.value, cached_data):
-                    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 cached_data
-
-        return self._parse_and_create_cache_file(path_name)
-
-    def _parse_and_create_cache_file(self, path_name):
-        """ When cache_id is None or cache doesn't exist, create a cache just for function metadata """
-        hosta_args = self._get_argsFunction(self.func)
-        with open(path_name, "wb") as f:
-            pickle.dump(hosta_args, f)
-        return hosta_args
-
-    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)
-        )
-
-        if self.cache_id is not None and self.value is not None:
-            if self.cache_id in self.infos_cache:
-                self.infos_cache[self.cache_id].append(self.value)
-            else:
-                self.infos_cache[self.cache_id] = [self.value]
-
-            self.infos_cache[f"{self.cache_id}_id"] = self._get_hashFunction(
-                str(self.infos_cache[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 _is_value_already_in_example(self, value, cached_data):
-        if self.cache_id not in cached_data:
-            print("Cache ID not found in cache file")
-            return False
-
-        def recursive_check(item, value):
-            if isinstance(item, dict):
-                if item == value or any(recursive_check(v, value) for v in item.values()):
-                    return True
-            elif isinstance(item, list):
-                return any(recursive_check(sub_item, value) for sub_item in item)
-            else:
-                return item == value
-
-        for item in cached_data[self.cache_id]:
-            if recursive_check(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_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"```python\ndef {func_name}({func_params}):{func_return}\n"
-            f"    \"\"\"\n\t{func.__doc__}\n    \"\"\"\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
\ No newline at end of file
diff --git a/DEPRECATED/utils/errors.py b/DEPRECATED/utils/errors.py
deleted file mode 100644
index 92bd1ff..0000000
--- a/DEPRECATED/utils/errors.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from typing import Literal
-
-__all__ = (
-    "RequestError",
-    "ApiKeyError",
-    "FrameError"
-)
-
-OhErrorCodes = Literal[
-    ""
-]
-
-class OhErrorMixin(Exception):
-    """ Base class for other customs exceptions """
-    def __init__(self, message:str):
-        super().__init__(message)
-        self.message = message
-        
-    def __str__(self)->str:
-        return f"{self.message}\n"
-    
-class RequestError(OhErrorMixin):
-    """ Raised when a request to a llm went wrong """
-
-class ApiKeyError(RequestError):
-    """ Raised when API key is missing or incorrect """
-
-class FrameError(OhErrorMixin):
-    """ Raised when the frame inspection fail """
\ No newline at end of file
diff --git a/DEPRECATED/utils/prompt.json b/DEPRECATED/utils/prompt.json
deleted file mode 100644
index 9e752fd..0000000
--- a/DEPRECATED/utils/prompt.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-    "prompts": [
-        {
-            "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": "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\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.2"
-        },
-        {
-            "key": "emulate",
-            "text": "## Context\n\nYou 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```\n\nThe output must be of the same type as that specified in the function call. If you don't have enough information or don't know how to answer, the output should be “None”. \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\n---\n\n## Examples\n\n**Example 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\n**Example emulated function call:**\n```python\nresult = example_function(3, {\\\"value\\\": 7})\n```\n\n**Expected JSON output:**\n{\"return\": 10}\n",
-            "category": "executive",
-            "version": "v1.2"
-        }
-    ]
-}
\ No newline at end of file
diff --git a/DEPRECATED/utils/prompt.py b/DEPRECATED/utils/prompt.py
deleted file mode 100644
index a6be5cd..0000000
--- a/DEPRECATED/utils/prompt.py
+++ /dev/null
@@ -1,97 +0,0 @@
-import os
-import json
-import sys
-
-
-class PromptMananger:
-    def __init__(self, json_path=None):
-        if json_path is None:
-            try:
-                self.path = os.path.join(os.path.dirname(__file__), "prompt.json")
-            except Exception as e:
-                self.path = ""
-                sys.stderr.write(f"[JSON_ERROR] Impossible to find prompt.json:\n{e}")
-                return
-        else:
-            self.path = json_path
-
-        try:
-            with open(self.path, "r", encoding="utf-8") as file:
-                self.json = json.load(file)
-                self.prompts = {item["key"]: item for item in self.json["prompts"]}
-        except FileNotFoundError:
-            sys.stderr.write(f"[JSON_ERROR] File not found: {self.path}\n")
-            self.prompts = {}
-        except json.JSONDecodeError as e:
-            sys.stderr.write(f"[JSON_ERROR] JSON decode error:\n{e}\n")
-            self.prompts = {}
-
-    def get_prompt(self, key):
-        prompt = self.prompts.get(key)
-        if prompt:
-            return prompt["text"]
-        sys.stderr.write(f"[JSON_ERROR] Prompt not found\n")
-        return None
-
-    def get_prompt_details(self, key):
-        prompt = self.prompts.get(key)
-        if prompt:
-            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/pyproject.toml b/pyproject.toml
index dc0c28d..4a5f427 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -15,7 +15,7 @@ authors = [
 ]
 readme = "README.md"
 license = {file = "LICENSE"}
-requires-python = ">=3.8"
+requires-python = ">=3.10,<3.13"
 classifiers = [
     "Development Status :: 5 - Production/Stable",
     "Programming Language :: Python :: 3.8",
@@ -25,10 +25,9 @@ classifiers = [
     "Natural Language :: French",
     "Topic :: Software Development :: Code Generators"
 ]
-dependencies = [
-  "requests>=2.32.3",
-  "pydantic>=2.8.2",
-]
+
+[project.optional-dependencies]
+pydantic = ["pydantic>=2.8.2"]
 
 [project.urls]
 Homepage = "https://github.com/hand-e-fr/OpenHosta"
diff --git a/src/OpenHosta/OpenHosta.py b/src/OpenHosta/OpenHosta.py
index efe6ae0..60d21ed 100644
--- a/src/OpenHosta/OpenHosta.py
+++ b/src/OpenHosta/OpenHosta.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 HOSTAPATH = "./"
 PROMPTPATH = "src/prompt.json"
 
@@ -10,18 +12,22 @@
 from .core import config
 from .exec.emulate import emulate
 from .exec.example import example
+from .exec.thought import thought
 from .exec.thinkof import thinkof
 from .utils.prompt import PromptManager
 from .exec.predict.predict import predict, ModelSchema
 from .exec.predict.architecture.builtins import ArchitectureType
+from .exec.ask import ask
 
 all = (
     "config",
     "emulate",
+    "thought",
     "example",
     "thinkof",
+    "ask",
     "PromptManager",
     "predict",
     "ModelSchema",
-    "ArchitectureType"
+    "ArchitectureType",
 )
diff --git a/src/OpenHosta/core/analizer.py b/src/OpenHosta/core/analizer.py
index 9b5850e..1638db1 100644
--- a/src/OpenHosta/core/analizer.py
+++ b/src/OpenHosta/core/analizer.py
@@ -1,110 +1,123 @@
 from __future__ import annotations
 
-from typing import Callable, Tuple, List, Dict, Any
-from typing import get_args, get_origin
-from pydantic import BaseModel, create_model
+from typing import Callable, Tuple, List, Dict, Any, Optional, Type
 import inspect
 from types import FrameType
 
+from ..utils.import_handler import is_pydantic
+
 all = (
     "_FuncInspector",
     "FuncAnalizer"
 )
 
 class _FuncInspector:
-    def __init__(self, func: Callable):
-        self.func = func
-        self.sig = inspect.signature(func)
-
-
-    def _get_function_signature(self):
-        func_name = self.func.__name__
-        func_params = ", ".join(
-            [
-                (
-                    f"{param_name}: {param.annotation.__name__}"
-                    if param.annotation != inspect.Parameter.empty and hasattr(param.annotation, '__name__')
-                    else param_name
-                )
-                for param_name, param in self.sig.parameters.items()
-            ]
-        )
-        func_return = (
-            f" -> {self.sig.return_annotation.__name__}"
-            if self.sig.return_annotation != inspect.Signature.empty and hasattr(self.sig.return_annotation, '__name__')
-            else ""
-        )
-        return func_name, func_params, func_return
+    """
+    A class for inspecting the signature, definition, and call information of a function.
 
+    Args:
+        func (Callable): The function to inspect.
 
-    def _get_function_definition(self):
-        func_name, func_params, func_return = self._get_function_signature()
-        definition = (
-            f"```python\ndef {func_name}({func_params}):{func_return}\n"
-            f"    \"\"\"\n    {self.func.__doc__}\n    \"\"\"\n```"
-        )
-        prototype = f"def {func_name}({func_params}):{func_return}"
-        return definition, prototype
+    Attributes:
+        func (Callable): The function to inspect.
+        sig (Signature): The signature of the function.
+    """
+    def __init__(self, func: Callable, caller_frame: FrameType):
+        """
+        Initialize the function inspector with the given function.
 
+        Args:
+            func (Callable): The function to inspect.
+            caller_frame (FrameType): The function's frame in which it is called.
+        """
+        try:
+            self.func = func
+            self.sig = inspect.signature(func)
+            _, _, _, self.values = inspect.getargvalues(caller_frame)
+        except Exception as e:
+            raise AttributeError(f"[_FuncInspector.__init__] Invalid arguments:\n{e}")
 
-    def _get_call_info(self, caller_frame):
-        _, _, _, values = inspect.getargvalues(caller_frame)
+    def _get_function_definition(self)->str:
+        """
+        Build the string representing the function's definition.
 
-        values_args = {k: v for k, v in values.items() if k in self.sig.parameters}
-        values_locals = {k: v for k, v in values.items() if k not in self.sig.parameters}
+        Returns:
+            The string representing the function's definition.
+        """
+        source = inspect.getsource(self.func)
+        definition = f"```python\n{source}\n```"
+        return definition
 
+
+    def _get_function_locals(self)->Tuple[dict, dict]:
+        """
+        Get the attributs and local variables of the function call.
+
+        Returns:
+            A tuple containing the bound arguments, local variables, and local attributs.
+        """
+        values_locals = {k: v for k, v in self.values.items() if k not in self.sig.parameters}
         values_locals.pop('self', None)
 
-        bound_args = self.sig.bind_partial(**values_args)
-        bound_args.apply_defaults()
+        try:
+            values_self = self.func.__self__.__dict__
+        except:
+            values_self = None    
+        return values_locals or None, values_self
 
-        return bound_args.arguments, values_locals or None
 
+    def _get_function_call(self)->Tuple[str, inspect.BoundArguments]:
+        """
+        Build a string representing the function call.
 
-    def _build_call_string(self, bound_arguments):
+        Returns:
+            A tuple containing a string representing the function call and the bound arguments.
+        """
+        values_args = {k: v for k, v in self.values.items() if k in self.sig.parameters}
+        
+        bound_args = self.sig.bind_partial(**values_args)
+        bound_args.apply_defaults()
+        
+        bound_arguments = bound_args.arguments
+        
         args_str = ", ".join(
             f"{k}={v!r}" for k, v in bound_arguments.items()
         )
         call_str = f"{self.func.__name__}({args_str})"
-        return call_str
+        return call_str, bound_arguments
+
+    def _get_function_type(self)->Tuple[List[Type[Any]], Type[Any]]:
+        """
+        Get the input and output types of the function.
 
-    def _get_function_type(self):
+        Returns:
+            A tuple containing the input types and output type.
+        """
         input_types = [param.annotation for param in self.sig.parameters.values()]
         output_type = self.sig.return_annotation if self.sig.return_annotation != inspect.Signature.empty else None
         return input_types, output_type
     
-    def _get_function_schema(self, func: Callable) -> Dict[str, Any]:
-        sig = inspect.signature(func)
-        return_caller = sig.return_annotation if sig.return_annotation != inspect.Signature.empty else None
-        return_schema = None
-
-        if return_caller is not None:
-            if get_origin(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("return_schema", annotation=(combined, ...))
-                return_schema = new_model.model_json_schema()
-            elif issubclass(return_caller, BaseModel):
-                return_schema = return_caller.model_json_schema()
-            else:
-                new_model = create_model("return_schema", annotation=(return_caller, ...))
-                return_schema = new_model.model_json_schema()
+    def _get_function_schema(self) -> Dict[str, Any]:
+        """
+        Get the JSON schema of the function's return type.
+
+        Returns:
+            The JSON schema of the function's return type.
+        """
+        if is_pydantic:
+            from .pydantic_usage import get_function_schema_pydantic
+            
+            return get_function_schema_pydantic(self)
         else:
-            No_return_specified = create_model(
-                "return_shema", annotation=(Any, ...)
-            )
-            return_schema = No_return_specified.model_json_schema()
-        return return_schema   
+            return {"type": "integer"}
 
 
 class FuncAnalizer(_FuncInspector):
     def __init__(self, func: Callable, caller_frame: FrameType):
-        super().__init__(func)
-        self.caller_frame = caller_frame
+        super().__init__(func, caller_frame)
 
     @property
-    def func_def(self) -> str:
+    def func_def(self) -> Optional[str]:
         """
         This method returns the function definition and his prototype as a string
         """
@@ -114,29 +127,17 @@ def func_def(self) -> str:
             raise AttributeError("[FuncAnalizer] Function definition not found")
 
     @property
-    def func_call(self) -> str:
+    def func_call(self) -> Optional[Tuple[str, inspect.BoundArguments]]:
         """
-        This method returns the function call as a string
+        This method returns the function call as a string and the bound arguments
         """
         try:
-            bound_args, _ = self._get_call_info(self.caller_frame)
-            return self._build_call_string(bound_args)
+            return self._get_function_call()
         except:
             raise AttributeError("[FuncAnalizer] Function call not found")
 
-    @property
-    def func_args(self) -> Dict[str, object]:
-        """
-        This method returns the function arguments as a dictionary
-        """
-        try:
-            bound_args, _ = self._get_call_info(self.caller_frame)
-            return bound_args
-        except:
-            raise AttributeError("[FuncAnalizer] Function arguments not found")
-
     @property 
-    def func_type(self) -> Tuple[List[type], type]:
+    def func_type(self) -> Optional[Tuple[List[Type[Any]], Type[Any]]]:
         """
         This method returns the function input and output types as a tuple
         """
@@ -146,22 +147,21 @@ def func_type(self) -> Tuple[List[type], type]:
             raise AttributeError("[FuncAnalizer] Function types not found")
 
     @property
-    def func_locals(self) -> dict:
+    def func_locals(self) -> Optional[Tuple[dict, dict]]:
         """
-        This method returns the function local variables as a dictionary
+        This method returns the function local variables and attributs.
         """
         try:
-            _, values_locals = self._get_call_info(self.caller_frame)
-            return values_locals
+            return self._get_function_locals()
         except:
             raise AttributeError("[FuncAnalizer] Function local variables not found")
         
     @property
-    def func_schema(self) -> dict:
+    def func_schema(self) -> Optional[Dict[str, Any]]:
         """
-        This method returns the function local variables as a dictionary
+        This method returns the return schema of the function.
         """
         try:
-            return self._get_function_schema(self.func)
+            return self._get_function_schema()
         except:
             raise AttributeError("[FuncAnalizer] Function schema cannot be created")
\ No newline at end of file
diff --git a/src/OpenHosta/core/checker.py b/src/OpenHosta/core/checker.py
index 3391336..183726a 100644
--- a/src/OpenHosta/core/checker.py
+++ b/src/OpenHosta/core/checker.py
@@ -1,32 +1,30 @@
 from __future__ import annotations
 
 from types import NoneType
-from typing import get_args, get_origin 
-from typing import Type, Any, Dict, Optional, Callable
-from pydantic import BaseModel
+from typing import Type, Any, Dict, Optional, Callable, TypeVar
 
+from ..utils.import_handler import is_pydantic
 from .hosta import Func
 
+T = TypeVar('T')
 
-def convert(typ: Type[Any]) -> Optional[Callable[[Any], Any]]:
-    return {
-        NoneType: lambda x: None,
-        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),
-        dict: lambda x: dict(x),
-        complex: lambda x: complex(x),
-        bytes: lambda x: bytes(x),
-    }.get(typ, lambda x: x)
 
 class HostaChecker:
-    
-    def __init__(self, func: Func, data:dict):
+    """
+    A class used to check and convert the output of a Language Model (LLM) to the type specified in a function's annotation.
+
+    Args:
+        func (Func): A function object that contains the type annotations for the LLM output.
+        data (dict): A dictionary containing the LLM output data to be checked and converted.
+
+    Attributes:
+        func (Func): The function object containing the type annotations for the LLM output.
+        data (dict): The LLM output data to be checked and converted.
+        checked (Any): The checked and converted data. If `data` contains a "return" key, its value is used as the checked data. Otherwise, `data` is used as the checked data.
+        is_passed (bool): A flag indicating whether the checked data should be converted or not. It is set to True if `data` contains a "return" key.
+    """
+
+    def __init__(self, func: Func, data: dict):
         self.func = func
         self.data = data
         try:
@@ -36,11 +34,55 @@ def __init__(self, func: Func, data:dict):
             self.checked = self.data
             self.is_passed = False
 
+    def _default(x: Any) -> Any:
+        """
+        A default conversion function that returns the input as is.
+
+        Args:
+            x (Any): The input data to be converted.
+
+        Returns:
+            Any: The input data as is.
+        """
+        return x
+
+    def convert(self, typ: Type[T]) -> Dict[Type[T], Optional[Callable[[Any], T]]]:
+        """
+        A method to create a conversion function for a given type.
 
+        Args:
+            typ (Type[T]): The type for which a conversion function needs to be created.
 
-    def convert_annotated(self)->Any:
+        Returns:
+            Dict[Type[T], Optional[Callable[[Any], T]]]: A dictionary mapping types to their corresponding conversion functions.
+        """
+        convertMap = {
+            NoneType: lambda x: None,
+            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),
+            dict: lambda x: dict(x),
+            complex: lambda x: complex(x),
+            bytes: lambda x: bytes(x),
+        }
+        if typ not in convertMap.keys():
+            return self._default.__func__
+        return convertMap[typ]
+
+    def convert_annotated(self) -> Any:
+        """
+        A method to convert the checked data based on the type annotations of the function.
+
+        Returns:
+            Any: The converted checked data based on the type annotations.
+        """
         if getattr(self.func.f_type[1], '__module__', None) == 'typing':
-            pass
+            pass  # Make a deep checking when annotated (see below)
         return self.checked
         #     origin = get_origin(self.func.f_type[1])
         #     args = get_args(self.func.f_type[1])
@@ -52,21 +94,21 @@ def convert_annotated(self)->Any:
         #     return self.checked
         # else:
         #     return self.checked
-        
-    def convert_pydantic(self)->Optional[BaseModel]:
-        try:
-            if issubclass(self.func.f_type[1], BaseModel):
-                return self.func.f_type[1](**self.checked)
-            return self.checked
-        except:
-            return self.checked
-        
-    def check(self)->Any:
+
+    def check(self) -> Any:
+        """
+        A method to check and convert the input data based on the function's type annotations and Pydantic model annotations.
+
+            Returns:
+                Any: The checked and converted data. If `data` contains a "return" key, its value is used as the checked data. Otherwise, `data` is used as the checked data.
+        """
         if self.checked == "None":
             return None
         if self.is_passed:
-            self.checked = convert(self.func.f_type[1])(self.checked)
+            self.checked = self.convert(self.func.f_type[1])(self.checked)
             self.checked = self.convert_annotated()
-            self.checked = self.convert_pydantic()
+            if is_pydantic:
+                from .pydantic_usage import convert_pydantic
+
+                self.checked = convert_pydantic(self.func.f_type[1], self.checked)
         return self.checked
-        
\ No newline at end of file
diff --git a/src/OpenHosta/core/config.py b/src/OpenHosta/core/config.py
index fc3450d..432b25b 100644
--- a/src/OpenHosta/core/config.py
+++ b/src/OpenHosta/core/config.py
@@ -1,15 +1,17 @@
 from __future__ import annotations
 
 import sys
-import requests
-from requests.exceptions import RequestException
-from requests.models import Response
+# import requests
+# from requests.exceptions import RequestException
+# from requests.models import Response
 import json
 import re
-from typing import Any, Optional
+from typing import Any, Dict
+from http.client import HTTPSConnection
+from urllib.parse import urlparse
 
 from ..utils.errors import ApiKeyError, RequestError
-from .hosta import Func
+from .pydantic_usage import Func
 from .checker import HostaChecker
 
 def is_valid_url(url:str)->bool:
@@ -46,9 +48,10 @@ def api_call(
         self,
         sys_prompt: str,
         user_prompt: str,
-        creativity: Optional[float] = None,
-        diversity: Optional[float] = None
-    )->Response:
+        json_form: bool = True,
+        **llm_args
+        
+    )->Dict:
         if self.api_key is None or not self.api_key:
             raise ApiKeyError("[model.api_call] Empty API key.")
 
@@ -64,34 +67,45 @@ def api_call(
                     "content": [{"type": "text", "text": user_prompt}],
                 },
             ],
-            "response_format": {"type": "json_object"},
-            "temperature": creativity if creativity is not None else 0.7,
-            "top_p": diversity if diversity is not None else 1,
         }
         headers = {
             "Content-Type": "application/json",
             "Authorization": f"Bearer {self.api_key}",
         }
-        self._last_request = l_body        
-
+        if json_form:
+            l_body["response_format"] = {"type": "json_object"}
+        for key, value in llm_args.items():
+            l_body[key] = value
         try:
-            response = requests.post(self.base_url, json=l_body, headers=headers)
-            response.raise_for_status()
-        except RequestException as e:
-            RequestException(f"[Model.api_call] Request failed:\n{e}\n\n")
-        if response.status_code != 200:
-            if "invalid_api_key" in str(response.text):
-                raise ApiKeyError("[Model.api_call] Incorrect API key.")
-            else:    
-                raise RequestError(f"[Model.api_call] API call was unsuccessful.\nStatus code: {response.status_code}:\n{response.text}")
-        self._nb_requests += 1
-        return response
-
-    def request_handler(self, response:Response, func:Func)->Any:
-        data = response.json()
-        json_string = data["choices"][0]["message"]["content"]
-        if "usage" in data:
-            self._used_tokens += int(data["usage"]["total_tokens"])
+            parsed_url = urlparse(self.base_url)
+            conn = HTTPSConnection(parsed_url.netloc)
+            
+            body_json = json.dumps(l_body)
+            conn.request("POST", parsed_url.path, body_json, headers)
+            
+            response = conn.getresponse()
+            response_data = response.read()
+            
+            conn.close()
+
+            if response.status != 200:
+                response_text = response_data.decode('utf-8')
+                if "invalid_api_key" in response_text:
+                    raise ApiKeyError("[Model.api_call] Incorrect API key.")
+                else:
+                    raise RequestError(
+                        f"[Model.api_call] API call was unsuccessful.\n"
+                        f"Status code: {response.status}:\n{response_text}"
+                    )
+            self._nb_requests += 1
+            return json.loads(response_data)
+        except Exception as e:
+            raise RequestError(f"[Model.api_call] Request failed:\n{e}\n\n")
+
+    def request_handler(self, response:Dict, func:Func)->Any:
+        json_string = response["choices"][0]["message"]["content"]
+        if "usage" in response:
+            self._used_tokens += int(response["usage"]["total_tokens"])
         try:
             l_ret_data = json.loads(json_string)
         except json.JSONDecodeError as e:
@@ -137,4 +151,4 @@ def set_default_model(new):
 def set_default_apiKey(api_key=None):
     DefaultManager.set_default_apiKey(api_key)
     
-# anntations, errors handling, doc
+# anntations, errors handling, doc, all
diff --git a/src/OpenHosta/core/hosta.py b/src/OpenHosta/core/hosta.py
index d223c49..fdc2c36 100644
--- a/src/OpenHosta/core/hosta.py
+++ b/src/OpenHosta/core/hosta.py
@@ -1,59 +1,22 @@
 from __future__ import annotations
 
-from typing import Dict, Any, Tuple, List, Optional, Literal, Union, TypedDict
-from pydantic import BaseModel, Field
-
+from typing import List, Optional
 
 from .inspector import HostaInspector
 from .analizer import FuncAnalizer
 from ..utils.errors import InvalidStructureError
+from ..utils.hosta_type import MemoryNode, MemKey, MemValue, ExampleType, CotType
+from .pydantic_usage import Func
 
 all = (
     "Hosta",
     "Func",
-    "ExampleType",
-    "CotType",
     "UseType",
     "MemKey",
     "MemValue",
-    "MemGet",
     "MemoryNode"
 )
 
-class ExampleType(TypedDict):
-    in_: Any
-    out: Any
-    
-class CotType(TypedDict):
-    pass
-
-class UseType(TypedDict):
-    pass
-
-MemKey = Literal["ex", "cot", "use"]
-MemValue = Union[CotType, ExampleType, UseType]
-    
-class MemoryNode(BaseModel):
-    key:MemKey
-    id:int
-    value:MemValue
-
-class Func(BaseModel):
-    """
-    Func is a Pydantic model representing a function's metadata.
-    Useful for the executive functions and the post-processing.
-    """
-    f_obj: Optional[object] = Field(default=None)
-    f_def: str = Field(default="", description="Simple definition of the function, e.g., 'def func(a:int, b:str)->int:'")
-    f_name: str = Field(default="", description="Name of the function, e.g., 'func'")
-    f_call: str = Field(default="", description="Actual call of the function, e.g., 'func(1, 'hello')'")
-    f_args: Dict[str, Any] = Field(default_factory=dict, description="Arguments of the function, e.g., {'a': 1, 'b': 'hello'}")
-    f_type: Tuple[List[Any], Any] = Field(default_factory=lambda: ([], None), description="Desired type of the input and output of the function")
-    f_schema: Dict[str, Any] = Field(default_factory=dict, description="Dictionary describing the function's return type (in case of pydantic).")
-    f_locals: Optional[Dict[str, Any]] = Field(default=None, description="Local variables within the function's scope")
-    f_mem: Optional[List[MemoryNode]] = Field(default=None, description="Memory nodes associated with the function, contains examples, chain of thought...")
-    
-
 class Hosta(HostaInspector):
     """
     Hosta is a class that extends HostaInspector and provides functionality for analyzing
@@ -129,13 +92,12 @@ def _get_infos_func(self) -> None:
         The extracted information is stored in the _infos attribute of the instance.
         """
         analizer = FuncAnalizer(self._obj[0], self._obj[1])
-        self._infos.f_obj   = self._obj[0]
+        self._infos.f_obj    = self._obj[0]
         self._infos.f_name   = self._obj[0].__name__
-        self._infos.f_def, _ = analizer.func_def
-        self._infos.f_call   = analizer.func_call
-        self._infos.f_args   = analizer.func_args
+        self._infos.f_def    = analizer.func_def
+        self._infos.f_call, self._infos.f_args = analizer.func_call
         self._infos.f_type   = analizer.func_type
-        self._infos.f_locals = analizer.func_locals
+        self._infos.f_locals, self._infos.f_self = analizer.func_locals
         self._infos.f_schema = analizer.func_schema
     
     def _bdy_add(self, key:MemKey, value:MemValue)->None:
diff --git a/src/OpenHosta/core/inspector.py b/src/OpenHosta/core/inspector.py
index 10431be..93543c6 100644
--- a/src/OpenHosta/core/inspector.py
+++ b/src/OpenHosta/core/inspector.py
@@ -140,8 +140,8 @@ def _attach(obj:Callable, attr: Dict[str, Any])->Optional[bool]:
         Return:
             Optional[bool]: Returns True if the attribute was successfully attached, raise an Exception otherwise. 
         """
-        if not callable(obj):
-            raise ValueError("[HostaInspector._attach] Invalid arguments: obj must a callable")
+        if not callable(obj) or not isinstance(attr, dict):
+            raise ValueError("[HostaInspector._attach] Invalid arguments")
 
         def attr_parser(obj:Callable, attr: Dict[str, Any])->bool:
             for key, value in attr.items():
diff --git a/src/OpenHosta/core/pydantic_usage.py b/src/OpenHosta/core/pydantic_usage.py
new file mode 100644
index 0000000..2b43ab2
--- /dev/null
+++ b/src/OpenHosta/core/pydantic_usage.py
@@ -0,0 +1,82 @@
+from __future__ import annotations
+
+from typing import Optional, Dict, Tuple, List, Any, get_args, get_origin
+import inspect
+
+from ..utils.import_handler import is_pydantic
+from ..utils.hosta_type import MemoryNode
+
+if is_pydantic:
+    from pydantic import BaseModel, Field, ConfigDict, create_model
+    
+    def convert_pydantic(caller, checked)->Optional[BaseModel]:
+        """
+        A method to convert the checked data based on the Pydantic model annotations of the function.
+
+        Returns:
+            Optional[BaseModel]: The converted checked data based on the Pydantic model annotations.
+        """
+        try:
+            if issubclass(caller, BaseModel):
+                return caller(**checked)
+            return checked
+        except:
+            return checked
+
+    class Func(BaseModel):
+        """
+        Func is a Pydantic model representing a function's metadata.
+        Useful for the executive functions and the post-processing.
+        """
+        model_config = ConfigDict(arbitrary_types_allowed=True)
+        f_obj: Optional[object] = Field(default=None)
+        f_def: str = Field(default="", description="Simple definition of the function, e.g., 'def func(a:int, b:str)->int:'")
+        f_name: str = Field(default="", description="Name of the function, e.g., 'func'")
+        f_call: str = Field(default="", description="Actual call of the function, e.g., 'func(1, 'hello')'")
+        f_args: Dict[str, Any] = Field(default_factory=dict, description="Arguments of the function, e.g., {'a': 1, 'b': 'hello'}")
+        f_type: Tuple[List[Any], Any] = Field(default_factory=lambda: ([], None), description="Desired type of the input and output of the function")
+        f_schema: Dict[str, Any] = Field(default_factory=dict, description="Dictionary describing the function's return type (in case of pydantic).")
+        f_locals: Optional[Dict[str, Any]] = Field(default=None, description="Local variables within the function's scope")
+        f_self: Optional[Dict[str, Any]] = Field(default=None)
+        f_mem: Optional[List[MemoryNode]] = Field(default=None, description="Memory nodes associated with the function, contains examples, chain of thought...")
+        
+    def get_function_schema_pydantic(self) -> Dict[str, Any]:
+        """
+        Get the JSON schema of the function's return type.
+
+        Returns:
+            The JSON schema of the function's return type.
+        """
+        return_caller = self.sig.return_annotation if self.sig.return_annotation != inspect.Signature.empty else None
+        return_schema = None
+
+        if return_caller is not None:
+            if get_origin(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("return_schema", annotation=(combined, ...))
+                return_schema = new_model.model_json_schema()
+            elif issubclass(return_caller, BaseModel):
+                return_schema = return_caller.model_json_schema()
+            else:
+                new_model = create_model("return_schema", annotation=(return_caller, ...))
+                return_schema = new_model.model_json_schema()
+        else:
+            No_return_specified = create_model(
+                "return_shema", annotation=(Any, ...)
+            )
+            return_schema = No_return_specified.model_json_schema()
+        return return_schema  
+else:
+    class Func:
+        f_obj: Optional[object] = None
+        f_def: str = ""
+        f_name: str = ""
+        f_call: str = ""
+        f_args: Dict[str, Any] = {}
+        f_type: Tuple[List[Any], Any] = ([], None)
+        f_schema: Dict[str, Any] = {}
+        f_locals: Optional[Dict[str, Any]] = None
+        f_self: Optional[Dict[str, Any]] = None
+        f_mem: Optional[List[MemoryNode]] = None
\ No newline at end of file
diff --git a/src/OpenHosta/exec/ask.py b/src/OpenHosta/exec/ask.py
new file mode 100644
index 0000000..a163c10
--- /dev/null
+++ b/src/OpenHosta/exec/ask.py
@@ -0,0 +1,33 @@
+from __future__ import annotations
+
+from typing import Any, Optional
+
+from ..core.config import Model, DefaultManager
+from ..utils.errors import RequestError
+
+def ask(
+    *,
+    user:str,
+    system:Optional[str] = None,
+    model:Optional[Model] = None,
+    **api_args
+)->Any:
+    if model is None:
+        model = DefaultManager.get_default_model()
+    if system is None:
+        system = "You are an helpful assistant."
+        
+    response = model.api_call(
+        system,
+        user,
+        False,
+        **api_args
+    )
+    
+    try:
+        data = response.json()
+        res = data["choices"][0]["message"]["content"]
+        setattr(ask, "_last_tokens", data["usage"]["total_tokens"])
+        return res
+    except Exception as e:
+        raise RequestError(f"[ask] Request failed:\n{e}")
\ No newline at end of file
diff --git a/src/OpenHosta/exec/emulate.py b/src/OpenHosta/exec/emulate.py
index 681db61..8f5d585 100644
--- a/src/OpenHosta/exec/emulate.py
+++ b/src/OpenHosta/exec/emulate.py
@@ -1,68 +1,78 @@
 from __future__ import annotations
 
-from typing import Any
-from wsgiref.validate import header_re
-
-from setuptools.sandbox import pushd
-from torch.fx.experimental.unification.unification_tools import assoc
+from typing import Any, Optional, Callable
 
 from ..core.hosta import Hosta, Func
 from ..core.config import Model, DefaultManager
 from ..utils.prompt import PromptManager
 
-def build_user_prompt(_infos: Func = None):
+
+def build_user_prompt(
+        _infos: Func = None,
+        x: Hosta = None,
+        use_locals_as_ctx: Optional[bool] = False,
+        use_self_as_ctx: Optional[bool] = False,
+):
     filler = lambda pre, value: f"**{pre}**\n{str(value)}\n\n" if value is not None and value != [] else ""
-    
+
     user_prompt = (
-        "---\n\n## Function infos\n\n"
-        + filler("Here's the function definition:", _infos.f_def)
-        + filler("Here's the function's locals variables which you can use as additional information to give your answer:", _infos.f_locals)
-        + filler("Here's the type annotation of the function:", _infos.f_type[1])
-        + filler("If it isn't a native type, here's a schema describing the type annotation:", _infos.f_schema)
-        # + filler("Here are some examples of expected input and output:", _infos["ho_example"])
-        + "---\n"
+            "---\n\n## Function infos\n\n"
+            + filler("Here's the function definition:", _infos.f_def)
+            + filler("Here's the type annotation of the function:", _infos.f_type[1])
+            + filler("If it isn't a native type, here's a schema describing the type annotation:", _infos.f_schema)
     )
+    if use_locals_as_ctx:
+        user_prompt = (user_prompt + filler("Here's the function's locals variables which you can use as additional information to give your answer:", _infos.f_locals))
+    if use_self_as_ctx:
+        user_prompt = (user_prompt + filler("Here's the method's class attributs variables which you can use as additional information to give your answer:", _infos.f_self))
+    if x:
+        user_prompt = (
+                user_prompt
+                + filler("Here are some examples of expected input and output:", x.example)
+                + filler("To solve the request, you have to follow theses intermediate steps. Give only the final result, don't give the result of theses intermediate steps:", x.cot)
+        )
+    user_prompt = (user_prompt + "---\n")
     return user_prompt
 
 
 def emulate(
-    model: Model = None,
-    l_creativity: float = None,
-    l_diversity: float = None,
-    _infos: Func = None
-)->Any:
+        *,
+        model: Optional[Model] = None,
+        use_locals_as_ctx: bool = False,
+        use_self_as_ctx: bool = False,
+        post_callback: Optional[Callable] = None,
+        _infos: Optional[Func] = None,
+        **llm_args
+) -> Any:
     x = None
-    l_ret:Any = None
+    l_ret: Any = None
     pm = PromptManager()
-    meta_prompt:str = pm.get_prompt("emulate")
-    
+    meta_prompt: str = pm.get_prompt("emulate")
+
     if _infos is None:
         x = Hosta()
-        _infos = x._infos  
-    func_prompt:str = build_user_prompt(_infos)
+        _infos = x._infos
+    func_prompt: str = build_user_prompt(_infos, x, use_locals_as_ctx, use_self_as_ctx)
 
     if model is None:
         model = DefaultManager.get_default_model()
-    if (l_creativity is not None and (l_creativity < 0 or l_creativity > 1)) or (
-        l_diversity is not None and (l_diversity < 0 or l_diversity > 1)
-    ):
-        raise ValueError("[emulate] Out of range values (0<creativity|diversity<1)")
-    
+
     try:
         response = model.api_call(
             sys_prompt=f"{meta_prompt}\n{func_prompt}\n",
             user_prompt=_infos.f_call,
-            creativity=l_creativity,
-            diversity=l_diversity,
+            **llm_args
         ),
         l_ret = model.request_handler(response[0], _infos)
+        if post_callback is not None:
+            l_ret = post_callback(l_ret)
     except NameError as e:
         raise NotImplementedError(f"[emulate]: {e}\nModel object does not have the required methods.")
-    
-    if x: 
+
+    if x:
         x._attach(_infos.f_obj, {
             "_last_request": f"{meta_prompt}\n{func_prompt}\n{_infos.f_call}",
-            "_last_response": response[0].json()
-            })
+            "_last_response": response[0]
+        })
 
     return l_ret
diff --git a/src/OpenHosta/exec/predict/model_schema.py b/src/OpenHosta/exec/predict/model_schema.py
index 4329769..8defff4 100644
--- a/src/OpenHosta/exec/predict/model_schema.py
+++ b/src/OpenHosta/exec/predict/model_schema.py
@@ -1,4 +1,4 @@
-from typing import Union
+from typing import Optional
 
 from .architecture.builtins import ArchitectureType
 
diff --git a/src/OpenHosta/exec/predict/predict.py b/src/OpenHosta/exec/predict/predict.py
index 89d14b1..247eca7 100644
--- a/src/OpenHosta/exec/predict/predict.py
+++ b/src/OpenHosta/exec/predict/predict.py
@@ -3,7 +3,6 @@
 
 from .data import PredictData
 from .model_schema import ModelSchema
-from .type_encoder.hosta_encoder import HostaEncoder
 from ...core.hosta import Hosta, Func
 from ...core.config import Model
 
@@ -38,7 +37,7 @@ def __init__(self, x: Hosta = None, model: ConfigModel = None, oracle: Optional[
                 raise ValueError(f"Return type must be one of [int, float, bool], not {self._infos.f_type[1]}")
             self._model: ConfigModel = model
             self._verbose: bool = verbose
-            self._encoder: HostaEncoder = HostaEncoder()
+            # self._encoder: HostaEncoder = HostaEncoder()
             self._data: PredictData = PredictData(path=os.path.join(os.path.dirname(__file__), "__hostacache__", str(hash(x))))
             self._initialized: bool = True
             self.oracle: Optional[Union[Model, Callable]] = oracle
diff --git a/src/OpenHosta/exec/thinkof.py b/src/OpenHosta/exec/thinkof.py
index 4f51e5a..0c498d1 100644
--- a/src/OpenHosta/exec/thinkof.py
+++ b/src/OpenHosta/exec/thinkof.py
@@ -23,7 +23,7 @@ def guess_type(key:str, *args)->object:
     response = l_default.api_call(
         sys_prompt=meta_prompt,
         user_prompt=l_user_prompt,
-        creativity=0.5,
+        temperature=0.5,
     )
 
     data = response.json()
diff --git a/src/OpenHosta/exec/thought.py b/src/OpenHosta/exec/thought.py
new file mode 100644
index 0000000..a6d2e5b
--- /dev/null
+++ b/src/OpenHosta/exec/thought.py
@@ -0,0 +1,7 @@
+from __future__ import annotations
+
+from ..core.hosta import Hosta, CotType
+
+def thought(task:str)->None:
+    x =  Hosta()
+    x._bdy_add('cot', CotType(task=task))
\ No newline at end of file
diff --git a/src/OpenHosta/exec/use.py b/src/OpenHosta/exec/use.py
new file mode 100644
index 0000000..f35da5f
--- /dev/null
+++ b/src/OpenHosta/exec/use.py
@@ -0,0 +1,29 @@
+from __future__ import annotations
+
+from typing import Union, Any, Callable, Union
+
+from ..core.hosta import Hosta, UseType
+
+all = (
+    "use",
+    "VAR",
+    "TOOL",
+    "RAG",
+    "DB"
+)
+
+class VAR:
+    pass
+
+class TOOL:
+    pass
+
+class RAG:
+    pass
+
+class DB:
+    pass
+
+def use(obj:Union[Callable, Any], typ:Union[VAR, TOOL, RAG, DB], title:str):
+    x = Hosta()
+    x._bdy_add('use', UseType())
diff --git a/src/OpenHosta/utils/errors.py b/src/OpenHosta/utils/errors.py
index 12c69c6..eda07e3 100644
--- a/src/OpenHosta/utils/errors.py
+++ b/src/OpenHosta/utils/errors.py
@@ -32,4 +32,7 @@ class FrameError(OhErrorMixin):
     """ Raised when the frame inspection fail """
     
 class InvalidStructureError(OhErrorMixin):
-    """ Raised when the bosy's function aren't placed correctly """
\ No newline at end of file
+    """ Raised when the bosy's function aren't placed correctly """
+    
+    
+# création d'agent + multimodal + tools
\ No newline at end of file
diff --git a/src/OpenHosta/utils/hosta_type.py b/src/OpenHosta/utils/hosta_type.py
new file mode 100644
index 0000000..cffb603
--- /dev/null
+++ b/src/OpenHosta/utils/hosta_type.py
@@ -0,0 +1,23 @@
+from __future__ import annotations
+
+from typing import Any, Literal, Union, TypedDict
+
+class ExampleType(TypedDict):
+    in_: Any
+    out: Any
+
+class CotType(TypedDict):
+    task: str
+ 
+class UseType(TypedDict):
+    pass
+
+MemKey = Literal["ex", "cot", "use"]
+MemValue = Union[CotType, ExampleType, UseType]
+    
+class MemoryNode:
+    key:MemKey
+    id:int
+    value:MemValue
+
+
diff --git a/src/OpenHosta/utils/import_handler.py b/src/OpenHosta/utils/import_handler.py
new file mode 100644
index 0000000..e21a441
--- /dev/null
+++ b/src/OpenHosta/utils/import_handler.py
@@ -0,0 +1,12 @@
+from __future__ import annotations
+
+all = (
+    "is_pydantic"
+)
+
+is_pydantic = False
+try:
+    import pydantic
+    is_pydantic = True
+except ImportError:
+    is_pydantic = False
\ No newline at end of file


Test patch:
diff --git a/src/test.py b/src/test.py
new file mode 100644
index 0000000..6a263cd
--- /dev/null
+++ b/src/test.py
@@ -0,0 +1,86 @@
+from typing import Any, Dict, get_args, get_origin, Union, List, Tuple
+import inspect
+from types import NoneType
+
+def _get_type_schema(tp: Any) -> Dict[str, Any]:
+    """
+    Generate a JSON schema for a given type.
+
+    Args:
+        tp: The type to generate the schema for.
+
+    Returns:
+        The JSON schema for the type.
+    """
+    print(tp)
+    if tp == Any:
+        return {"type": "any"}
+    
+    origin = get_origin(tp)
+    args = get_args(tp)
+    
+    if origin is Union:
+        return {"anyOf": [_get_type_schema(arg) for arg in args]}
+    
+    if origin is list or origin is List:
+        return {
+            "type": "array",
+            "items": _get_type_schema(args[0]) if args else {"type": "any"}
+        }
+    
+    if origin is dict or origin is Dict:
+        return {
+            "type": "object",
+            "additionalProperties": _get_type_schema(args[1]) if args else {"type": "any"}
+        }
+    
+    if origin is tuple or origin is Tuple:
+        return {
+            "type": "array",
+            "prefixItems": [_get_type_schema(arg) for arg in args],
+            "items": False
+        }
+    
+    if tp is int:
+        return {"type": "integer"}
+    if tp is float:
+        return {"type": "float"}
+    if tp is str:
+        return {"type": "string"}
+    if tp is bool:
+        return {"type": "boolean"}
+    if tp is NoneType:
+        return {"type": "null"}
+    
+    raise ValueError(f"Unsupported type: {tp}")
+
+def _get_function_schema_manual(func) -> Dict[str, Any]:
+    """
+    Get the JSON schema of the function's return type.
+
+    Returns:
+        The JSON schema of the function's return type.
+    """
+    sig = inspect.signature(func)
+    return_caller = sig.return_annotation if sig.return_annotation != inspect.Signature.empty else None
+    
+    if return_caller is not None:
+        return _get_type_schema(return_caller)
+    else:
+        return _get_type_schema(Any)   
+    
+def test1()->None:
+    return
+
+def test2()->Tuple[List[bool], List[bool]]:
+    return
+
+class TMP():
+    var:str = ""
+    
+def test3()->TMP:
+    return
+
+print(_get_function_schema_manual(test1))
+print()
+print(_get_function_schema_manual(test2))
diff --git a/test_pyd.py b/test_pyd.py
new file mode 100644
index 0000000..264c24a
--- /dev/null
+++ b/test_pyd.py
@@ -0,0 +1,14 @@
+from OpenHosta import config, emulate
+
+config.set_default_apiKey("")
+
+def multiply(a:int, b:int):
+    """
+    This function multiplies two integers in parameter.
+    """
+    return emulate()
+
+res = multiply(5, 6)
+print(res)
+print(type(res))
+print(multiply._last_request)
\ No newline at end of file
diff --git a/tests/functionnalTests/test_ask.py b/tests/functionnalTests/test_ask.py
new file mode 100644
index 0000000..bb377e8
--- /dev/null
+++ b/tests/functionnalTests/test_ask.py
@@ -0,0 +1,1 @@
+import pytest
\ No newline at end of file
diff --git a/tests/functionnalTests/test_bdy.py b/tests/functionnalTests/test_bdy.py
new file mode 100644
index 0000000..bb377e8
--- /dev/null
+++ b/tests/functionnalTests/test_bdy.py
@@ -0,0 +1,1 @@
+import pytest
\ No newline at end of file
diff --git a/tests/functionnalTests/test_emulate.py b/tests/functionnalTests/test_emulate.py
new file mode 100644
index 0000000..5871ed8
--- /dev/null
+++ b/tests/functionnalTests/test_emulate.py
@@ -0,0 +1,1 @@
+import pytest
diff --git a/tests/functionnalTests/test_mandatory.py b/tests/functionnalTests/test_mandatory.py
index b780887..dabdf4c 100644
--- a/tests/functionnalTests/test_mandatory.py
+++ b/tests/functionnalTests/test_mandatory.py
@@ -1,313 +1,310 @@
-# import pytest
-# import time as t
-# from pydantic import BaseModel
-# from typing import Callable
-# import numpy as np
+import pytest
+import time as t
+from pydantic import BaseModel
+from typing import Callable
+import numpy as np
 
-# import OpenHosta.OpenHosta
-# import OpenHosta.config as config
-# from OpenHosta.emulate import emulate
-# from OpenHosta.example import example
+from OpenHosta import config, emulate, thinkof, example
 
-# g_apiKey = ""
+g_apiKey = ""
 
-# config.set_default_apiKey(g_apiKey)
+config.set_default_apiKey(g_apiKey)
 
-# def emulate_1arg_str(arg)->str:
-#     """ Docstring """
-#     return emulate()
+def emulate_1arg_str(arg)->str:
+    """ Docstring """
+    return emulate()
 
-# def emulate_1arg_int(arg)->int:
-#     """ Docstring """
-#     return emulate()
+def emulate_1arg_int(arg)->int:
+    """ Docstring """
+    return emulate()
 
-# def emulate_1arg_float(arg)->float:
-#     """ Docstring """
-#     return emulate()
+def emulate_1arg_float(arg)->float:
+    """ Docstring """
+    return emulate()
 
-# def emulate_1arg_list(arg)->list:
-#     """ 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}
+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 User(BaseModel):
+    name: str
+    email: str
+    age: int
 
-# class TestPredict:
-#     def test_basic_input_predict(self):
-#         def predict_1(a: int) -> float:
-#             """
-#             This function predicts the id of a letter
-#             """
-#             example(1, hosta_out=1.0)
-#             example(2, hosta_out=2.0)
-#             example(3, hosta_out=3.0)
-#             return predict()
+class TestPredict:
+    def test_basic_input_predict(self):
+        def predict_1(a: int) -> float:
+            """
+            This function predicts the id of a letter
+            """
+            example(1, hosta_out=1.0)
+            example(2, hosta_out=2.0)
+            example(3, hosta_out=3.0)
+            return predict()
         
-#         ret_1 = predict_1(1)
-#         assert type(ret_1) == float
+        ret_1 = predict_1(1)
+        assert type(ret_1) == float
 
 
-#     def test_mutiple_input_predict(self):
-#         def predict_2(a: int, b:int) -> float:
-#             """
-#             This function predict the id of sum of two letter
-#             """
-#             example(a=1, b=2, hosta_out=3.0, hosta_func=predict_2)
-#             return predict()
-#         ret_2 = predict_2(1, 2)
-#         assert type(ret_2) == float
+    def test_mutiple_input_predict(self):
+        def predict_2(a: int, b:int) -> float:
+            """
+            This function predict the id of sum of two letter
+            """
+            example(a=1, b=2, hosta_out=3.0, hosta_func=predict_2)
+            return predict()
+        ret_2 = predict_2(1, 2)
+        assert type(ret_2) == float
 
-#     def test_attributs_predict(self, capsys):
-#         def predict_3(a:int) -> float:
-#             """
-#             transform an int into a float
-#             """
-#             example(1, hosta_out=1.0)
-#             return predict()
+    def test_attributs_predict(self, capsys):
+        def predict_3(a:int) -> float:
+            """
+            transform an int into a float
+            """
+            example(1, hosta_out=1.0)
+            return predict()
 
-#         ret = predict_3(1)
-#         predict_3.retrain(epochs=15, verbose=False)
-#         ret_after = predict_3(1)
-#         assert ret != ret_after
+        ret = predict_3(1)
+        predict_3.retrain(epochs=15, verbose=False)
+        ret_after = predict_3(1)
+        assert ret != ret_after
 
-#         ret_emulate = predict_3.emulate(1)
-#         print(ret_emulate)
-#         assert type(ret_emulate) == float
+        ret_emulate = predict_3.emulate(1)
+        print(ret_emulate)
+        assert type(ret_emulate) == float
 
-#         predict_3.continue_train(epochs=1500, get_loss=0.01, verbose=True)
-#         stdout = capsys.readouterr()
-#         assert "Loss target achieved at epoch" in stdout.out
+        predict_3.continue_train(epochs=1500, get_loss=0.01, verbose=True)
+        stdout = capsys.readouterr()
+        assert "Loss target achieved at epoch" in stdout.out
 
-# class TestTrainingset:
-#     def test_training_function(self):
-#         len_sublist = 0
-#         def function_to_test(a: int, b: int) -> float:
-#             """
-#             This function returns the sum of two integers.
-#             """
-#             return predict()
-#         example(a=1, b=2, hosta_out=3.0, hosta_func=function_to_test)
+class TestTrainingset:
+    def test_training_function(self):
+        len_sublist = 0
+        def function_to_test(a: int, b: int) -> float:
+            """
+            This function returns the sum of two integers.
+            """
+            return predict()
+        example(a=1, b=2, hosta_out=3.0, hosta_func=function_to_test)
 
-#         training_test = TrainingSet(function_to_test)
-#         training_test.add(a=2, b=3, hosta_out=5.0)
-#         dataset = training_test.visualize()
+        training_test = TrainingSet(function_to_test)
+        training_test.add(a=2, b=3, hosta_out=5.0)
+        dataset = training_test.visualize()
 
-#         expected_values = [{'a': 1, 'b': 2, 'hosta_out': 3}, {'a': 2, 'b': 3, 'hosta_out': 5}]
+        expected_values = [{'a': 1, 'b': 2, 'hosta_out': 3}, {'a': 2, 'b': 3, 'hosta_out': 5}]
 
-#         for expected in expected_values:
-#             assert expected in dataset[0] or expected in dataset[1], f"{expected} n'est pas présent dans le dataset"
+        for expected in expected_values:
+            assert expected in dataset[0] or expected in dataset[1], f"{expected} n'est pas présent dans le dataset"
 
-# class TestEmulate:
+class TestEmulate:
        
-#     def test_FeatureModelInParameter(self):
-#         global g_apiKey
+    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
-#         )
+        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)
+        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   
+        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)
+    @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_FeaturesEmptyInfos(self):
+        def a():
+            return emulate()
+        res = a()
+        assert res is None
         
-#     def test_FeatureDecorator(self):
+    def test_FeatureDecorator(self):
         
-#         def decorator(func):
-#             def wrapper(*args, **kwargs):
-#                 res = func(*args, **kwargs)
-#                 return res
-#             return wrapper
+        def decorator(func):
+            def wrapper(*args, **kwargs):
+                res = func(*args, **kwargs)
+                return res
+            return wrapper
         
-#         @decorator
-#         def returntNone():
-#             """ This function returns None """
-#             return emulate()
+        @decorator
+        def returntNone():
+            """ This function returns None """
+            return emulate()
         
-#         ret = returntNone()
-#         assert ret is None
+        ret = returntNone()
+        assert ret is None
         
-#     def test_FeatureClassMethod(self):
+    def test_FeatureClassMethod(self):
         
-#         class EmulateClass:
+        class EmulateClass:
             
-#             def returntThree(self)->int:
-#                 """ This function returns 3 """
-#                 return emulate()
+            def returntThree(self)->int:
+                """ This function returns 3 """
+                return emulate()
             
-#         x = EmulateClass()
-#         ret = x.returntThree()
-#         assert ret == 3
+        x = EmulateClass()
+        ret = x.returntThree()
+        assert ret == 3
         
-#     def test_FeatureClassMagicMethod(self, capsys):
+    def test_FeatureClassMagicMethod(self, capsys):
         
-#         class EmulateClass:
+        class EmulateClass:
             
-#             def __str__(self):
-#                 """ This function returns "Hello World!" """
-#                 return emulate()
+            def __str__(self):
+                """ This function returns "Hello World!" """
+                return emulate()
             
-#         x = EmulateClass()
-#         print(x)
-#         stdout = capsys.readouterr()
-#         assert "Hello World!" in stdout.out
+        x = EmulateClass()
+        print(x)
+        stdout = capsys.readouterr()
+        assert "Hello World!" in stdout.out
         
-#     def test_FeatureLocalVariables(self):
+    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()
+        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
+        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 test_FeatureNestedFunction(self):
             
-#             def grandFunction():
-#                 def returnHelloWorld():
-#                     """ 
-#                     This function returns "Hello World!".
-#                     """
-#                     return emulate()
-#                 return returnHelloWorld
+            def grandFunction():
+                def returnHelloWorld():
+                    """ 
+                    This function returns "Hello World!".
+                    """
+                    return emulate()
+                return returnHelloWorld
             
-#             nested = grandFunction()
-#             ret = nested()
-#             assert ret == "Hello World!" 
+            nested = grandFunction()
+            ret = nested()
+            assert ret == "Hello World!" 
         
-#     def test_FeaturePydanticGlobal(self):
+    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()
+        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
+        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_FeaturePydanticLocal(self):
+        pass
         
-#     def test_rc2(self):
+    def test_rc2(self):
         
-#         def multiply(a:int, b:int)->int:
-#             """
-#             This function multiplies two integers in parameter.
-#             """
-#             return emulate()
+        def multiply(a:int, b:int)->int:
+            """
+            This function multiplies two integers in parameter.
+            """
+            return emulate()
         
-#         def main():
-#             return multiply(2 ,3)
+        def main():
+            return multiply(2 ,3)
             
-#         res = main()
-#         assert res == 6
+        res = main()
+        assert res == 6
 
-# class TestThought:
+class Testthinkof:
     
-#     def test_BasicDirect(self):
-#         ret = thought("Multiply by 2")(8)
-#         assert ret == 16
+    def test_BasicDirect(self):
+        ret = thinkof("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!"
+    def test_BasicIndirect(self):
+        x = thinkof("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", [
+        ("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 = thinkof(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
+    @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 = thinkof(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_FeatureMultiArgs(self):
+        x = thinkof("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_FeatureChainOfthinkof(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
-#         )
+    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)
+        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
+        x = thinkof("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
\ No newline at end of file
+    def test_FeatureCachedPrediction(self):
+        pass
+class TestTypingPydantic:
+    pass
\ No newline at end of file
diff --git a/tests/functionnalTests/test_predict.py b/tests/functionnalTests/test_predict.py
new file mode 100644
index 0000000..bb377e8
--- /dev/null
+++ b/tests/functionnalTests/test_predict.py
@@ -0,0 +1,1 @@
+import pytest
\ No newline at end of file
diff --git a/tests/functionnalTests/test_thinkof.py b/tests/functionnalTests/test_thinkof.py
new file mode 100644
index 0000000..bb377e8
--- /dev/null
+++ b/tests/functionnalTests/test_thinkof.py
@@ -0,0 +1,1 @@
+import pytest
\ No newline at end of file
diff --git a/DEPRECATED/body/__init__.py b/tests/runner.py
similarity index 100%
rename from DEPRECATED/body/__init__.py
rename to tests/runner.py
diff --git a/tests/unitTests/test_analizer.py b/tests/unitTests/test_analizer.py
new file mode 100644
index 0000000..4e507c4
--- /dev/null
+++ b/tests/unitTests/test_analizer.py
@@ -0,0 +1,162 @@
+import pytest
+import inspect
+import re
+from typing import Optional, List, Union
+from pydantic import BaseModel, Field
+from PIL import Image
+
+from OpenHosta.core.analizer import FuncAnalizer as FA
+
+
+class TestInspector:
+    
+    def test_InitSimple(self):
+        def tmp():
+            return inspect.currentframe()
+        x = FA(tmp, tmp())
+        assert type(x) == FA
+        
+    def test_InitInvalidArgs(self):
+        with pytest.raises(AttributeError, match=re.escape("[_FuncInspector.__init__] Invalid arguments:\n'hi' is not a callable object")):
+            FA("hi", 3)
+            
+    def test_FuncDef(self):
+        def testfunc():
+            return inspect.currentframe()
+    
+        frame = testfunc()
+        x = FA(testfunc, frame)
+        my = "```python\n        def testfunc():\n            return inspect.currentframe()\n\n```"
+        assert x.func_def == my
+        
+    def test_FuncLocalsNoVal(self):
+        def testfunc():
+            return inspect.currentframe()
+    
+        frame = testfunc()
+        x = FA(testfunc, frame)
+        assert x.func_locals == (None, None)
+        
+    def test_FuncLocalsSimple(self):
+        def testfunc():
+            a = 2
+            b = "hi"
+            return inspect.currentframe()
+    
+        frame = testfunc()
+        x = FA(testfunc, frame)
+        assert x.func_locals == ({"a": 2, "b": "hi"}, None)
+
+    def test_FuncLocalsSelf(self):
+        class TMP:
+            def __init__(self):
+                self.c = 3.14
+            
+            def testfunc(self):
+                a = 2
+                b = "hi"
+                return inspect.currentframe()
+    
+        tmp = TMP()
+        frame = tmp.testfunc()
+        x = FA(tmp.testfunc, frame)
+        assert x.func_locals == ({"a": 2, "b": "hi"}, {"c": 3.14})
+        
+    def test_FuncCallSimple(self):
+        def testfunc(a:int, b:str):
+            return inspect.currentframe()
+    
+        frame = testfunc(2, "hi")
+        x = FA(testfunc, frame)
+        assert x.func_call == ("testfunc(a=2, b='hi')", {'a': 2, 'b': 'hi'})
+
+    def test_FuncCallNoArgs(self):
+        def testfunc():
+            return inspect.currentframe()
+    
+        frame = testfunc()
+        x = FA(testfunc, frame)
+        assert x.func_call == ("testfunc()", {})
+        
+    def test_FuncCallComplexArgs(self):
+        class TMP(BaseModel):
+            var:str = Field(default="", description="It's a var :)")
+        def testfunc(a:TMP, b:Image):
+            return inspect.currentframe()
+    
+        ins = TMP()
+        img = Image.new('RGB', (10, 10), 'white')
+        frame = testfunc(ins, img)
+        x = FA(testfunc, frame)
+        assert x.func_call == (f'testfunc(a={ins!r}, b={img!r})', {"a": ins, "b": img})
+    
+    def test_FuncTypeSimple(self):
+        def testfunc(a:int, b:str)->list:
+            return inspect.currentframe()
+    
+        frame = testfunc(2, "hi")
+        x = FA(testfunc, frame)
+        assert x.func_type == ([int, str], list)
+        
+    def test_FuncTypeArgsWithNoType(self):
+        def testfunc(a, b):
+            return inspect.currentframe()
+    
+        frame = testfunc(2, "hi")
+        x = FA(testfunc, frame)
+        assert x.func_type == ([inspect._empty, inspect._empty], None)
+        
+    def test_FuncTypeNoArgs(self):
+        def testfunc():
+            return inspect.currentframe()
+    
+        frame = testfunc()
+        x = FA(testfunc, frame)
+        assert x.func_type == ([], None)
+        
+    def test_FuncTypeComplexArgs(self):
+        class TMP(BaseModel):
+            var:str = Field(default="", description="It's a var :)")
+        def testfunc(a:TMP, b:Image)->Optional[List[int]]:
+            return inspect.currentframe()
+    
+        ins = TMP()
+        img = Image.new('RGB', (10, 10), 'white')
+        frame = testfunc(ins, img)
+        x = FA(testfunc, frame)
+        assert x.func_type == ([type(ins), Image], Optional[List[int]])
+        
+    def test_FuncSchemaBuiltIn(self):
+        def testfunc(a:int, b:int)->list:
+            return inspect.currentframe()
+        
+        frame = testfunc(2, "hi")
+        x = FA(testfunc, frame)
+        assert x.func_schema == {'properties': {'annotation': {'items': {}, 'title': 'Annotation', 'type': 'array'}}, 'required': ['annotation'], 'title': 'return_schema', 'type': 'object'}
+    
+    def test_FuncSchemaAnnotated(self):
+        def testfunc(a:Union[int, str])->Optional[List[float]]:
+            return inspect.currentframe()
+        
+        frame = testfunc(2)
+        x = FA(testfunc, frame)
+        assert x.func_schema == {'properties': {'annotation': {'anyOf': [{'items': {'type': 'number'}, 'type': 'array'}, {'type': 'null'}], 'title': 'Annotation'}}, 'required': ['annotation'], 'title': 'return_schema', 'type': 'object'}
+    
+    def test_FuncSchemaPydantic(self):
+        class TMP(BaseModel):
+            var1:str = Field(default="", description="It's one var :)")
+            var2:int = Field(default=0, description="It's two var :)")
+        def testfunc(a:TMP)->TMP:
+            return inspect.currentframe()
+        
+        frame = testfunc(2)
+        x = FA(testfunc, frame)
+        assert x.func_schema == {'properties': {'var1': {'default': '', 'description': "It's one var :)", 'title': 'Var1', 'type': 'string'}, 'var2': {'default': 0, 'description': "It's two var :)", 'title': 'Var2', 'type': 'integer'}}, 'title': 'TMP', 'type': 'object'}
+    
+    def test_FuncSchemaNoType(self):
+        def testfunc():
+            return inspect.currentframe()
+        
+        frame = testfunc()
+        x = FA(testfunc, frame)
+        assert x.func_schema == {'properties': {'annotation': {'title': 'Annotation'}}, 'required': ['annotation'], 'title': 'return_shema', 'type': 'object'}
diff --git a/tests/unitTests/test_checker.py b/tests/unitTests/test_checker.py
new file mode 100644
index 0000000..bb377e8
--- /dev/null
+++ b/tests/unitTests/test_checker.py
@@ -0,0 +1,1 @@
+import pytest
\ No newline at end of file
diff --git a/tests/unitTests/test_config.py b/tests/unitTests/test_config.py
new file mode 100644
index 0000000..bb377e8
--- /dev/null
+++ b/tests/unitTests/test_config.py
@@ -0,0 +1,1 @@
+import pytest
\ No newline at end of file
diff --git a/tests/unitTests/test_exec.py b/tests/unitTests/test_exec.py
deleted file mode 100644
index e5e5063..0000000
--- a/tests/unitTests/test_exec.py
+++ /dev/null
@@ -1,69 +0,0 @@
-import sys
-import pytest
-from unittest.mock import patch
-import inspect
-
-from DEPRECATED import exec
-
-class TestExtendScope:
-    
-    def test_basic(self):
-        def printHello():
-            print("Hello")
-
-        x =  exec.HostaInjector(printHello)
-
-        class Test:
-            def test(self):
-                return x._extend_scope()
-
-        def caller():
-            y = Test()
-            return y.test()
-
-        res = caller()
-        assert res[0] == caller
-        
-    def test_Flask(self):
-        from flask import Flask, request, session
-        
-        def printHello():
-            print("Hello")
-
-        x =  exec.HostaInjector(printHello)
-
-        class Test:
-            def test(self):
-                return x._extend_scope()
-
-        def caller():
-            y = Test()
-            return y.test()
-
-        res = caller()
-        del sys.modules["flask"]
-        assert res[0] == caller
-        
-    def test_ErrorHandling(self):
-        def printHello():
-            print("Hello")
-
-        x =  exec.HostaInjector(printHello)
-
-        class Test:
-            def test(self):
-                return x._extend_scope()
-
-        def caller():
-            y = Test()
-            return y.test()
-
-        current = inspect.currentframe()
-        print(f"INTERNAL: {current.f_locals}")
-        current.f_locals["caller"] = "hello"
-        current.f_locals.pop("caller")
-        print(f"INTERNAL: {current.f_locals}")
-
-        with patch("exec.hasattr", return_value=False):
-            res = caller()
-        assert res == caller
\ No newline at end of file
diff --git a/tests/unitTests/test_hosta.py b/tests/unitTests/test_hosta.py
new file mode 100644
index 0000000..b53647a
--- /dev/null
+++ b/tests/unitTests/test_hosta.py
@@ -0,0 +1,3 @@
+import pytest
+
+        
\ No newline at end of file
diff --git a/tests/unitTests/test_inspector.py b/tests/unitTests/test_inspector.py
new file mode 100644
index 0000000..e0943b6
--- /dev/null
+++ b/tests/unitTests/test_inspector.py
@@ -0,0 +1,108 @@
+import pytest
+import inspect
+import re
+import sys
+from unittest.mock import patch
+
+from OpenHosta.core.inspector import HostaInspector as HI
+from OpenHosta.utils.errors import FrameError
+
+class TestExtend:
+    
+    def test_BasicMethod(self):
+        class TMP:
+            def tmp_extend(self):
+                return HI._extend()
+        def tmp():
+            x = TMP()
+            return x.tmp_extend()
+        def tmp2():
+            return (tmp(), inspect.currentframe())
+        
+        res = tmp2()
+        assert res[0] == (tmp2, res[1])
+        
+    def test_BasicFunc(self):
+        pass
+        
+    def test_LowBackLevel(self):    
+        def tmp():
+            return (HI._extend(back_level=1), inspect.currentframe())
+        
+        res = tmp()
+        
+        assert res[0] == (tmp, res[1])
+        
+    def test_InvalidArgs(self):
+        with pytest.raises(ValueError, match=re.escape("[HostaInspector._extend] back_level must a non-zero positive integers.")):
+            HI._extend(back_level=0)
+            HI._extend(back_level=-1)
+            HI._extend(back_level="Hi mom")
+    
+    def test_FuncWithFlask(self):
+        from flask import Flask, request, session
+
+        def tmp():
+            return (HI._extend(back_level=1), inspect.currentframe())
+        
+        res = tmp()
+        del sys.modules["flask"]
+        assert res[0] == (tmp, res[1])
+
+    def test_FuncWithGlobals(self):
+        pass # TODO
+    
+    def test_FBackNotFound(self):
+        with pytest.raises(FrameError, match=re.escape("[HostaInspector._extend] Frame can't be found (level: 33)")):
+            HI._extend(back_level=100)
+    
+    def test_FuncNotCallable(self):
+        pass # TODO
+    
+class TestAttach:
+    
+    def test_OneAttr(self):
+        def tmp():
+            return
+        HI._attach(tmp, {"nb": 3})
+        assert tmp.nb == 3
+            
+    def test_ManyAttr(self):
+        def tmp():
+            return
+        HI._attach(tmp, {
+            "a": 1,
+            "b": "hi",
+            "c": tmp
+        })
+        assert tmp.a == 1
+        assert tmp.b == "hi"
+        assert tmp.c == tmp
+    
+    def test_InvalidArgs(self):
+        def tmp():
+            return
+        with pytest.raises(ValueError, match=re.escape("[HostaInspector._attach] Invalid arguments")):
+            HI._attach(tmp, "6")
+            HI._attach("a", 1)
+    
+    def test_BasicMethod(self):
+        class TMP:
+            def tmp(self):
+                return
+        x = TMP()
+        HI._attach(x.tmp, {"a": 1})
+        assert x.tmp.a == 1
+    
+    def test_InvalidMethod(self):
+        def tmp():
+            return
+        with patch('inspect.ismethod', return_value=True):
+            with pytest.raises(AttributeError, match=re.escape("[HostaInspector._attach] Failed to attach attributs. \"__func__\" attribut is missing.")):
+                HI._attach(tmp, {"a": 1})
+    
+    def test_InvalidCallable(self):
+        class TMP:
+            pass
+        with pytest.raises(AttributeError, match=re.escape(f"[HostaInspector._attach] Failed to attach attributs. Object's type not supported: {type(TMP)}.")):
+            HI._attach(TMP, {"a": 1})