textjenerator 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Warren Davies
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,112 @@
1
+ Metadata-Version: 2.4
2
+ Name: textjenerator
3
+ Version: 0.1.0
4
+ Summary: AI text generator
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Author: Warren Davies
8
+ Requires-Python: >=3.10,<3.15
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
14
+ Requires-Dist: llama-cpp-python (>=0.3.16,<0.4.0)
15
+ Requires-Dist: torch (>=2.9.1,<3.0.0)
16
+ Description-Content-Type: text/markdown
17
+
18
+ # Text Jenerator
19
+
20
+ A Unified Python Interface for Modular Text Generation.
21
+
22
+ Text Jenerator is a framework designed to provide a consistent, abstract interface for deploying and interacting with various Large Language Models (LLMs). By separating the generic configuration and workflow from the model-specific implementation, it enables you to easily switch between different model backends—such as local GGUF models (via Llama-CPP), Hugging Face Transformers, or proprietary APIs (e.g., OpenAI, Gemini), with no code changes besides the config you pass.
23
+
24
+ ## Features
25
+
26
+ * **Modular Architecture**: Uses Abstract Base Classes (ABC) to enforce a clean separation between the model details, meaning you only need to instantiate the TextGenerator class and call `generate_text()`, no matter which model you're using.
27
+ * **Automatic Setup**: Handles generic tasks like device detection (CPU/CUDA) and data type selection (bfloat16, float16, float32) in the base class.
28
+ * **Plug-and-Play Models**: Easily integrate new models by creating concrete child classes that implement two simple abstract methods (create_pipeline and run_pipeline).
29
+ * **Model Registry**: Uses a decorator (@register_model) to automatically track and load available generation backends.
30
+
31
+ ## Installation
32
+
33
+ ```sh
34
+ pip install textjenerator
35
+ ```
36
+
37
+ The core package has minimal dependencies. Model-specific implementations (e.g., for Llama-CPP or Transformers) require their respective libraries.
38
+
39
+ ## Models
40
+
41
+ Text Jenerator is BYOM. You can put the local model path or a HuggingFace ID in your config.
42
+
43
+ ## Quick Start & Usage
44
+
45
+ ### Config
46
+
47
+ Your project should include a config file (defaults are at textjenerator.config). A basic config might look like this:
48
+
49
+ ```json
50
+ config = {
51
+ # model
52
+ "model": "llama-cpp",
53
+ "model_path": ".models/Llama-3.2-3B-Instruct-Q4_K_M.gguf",
54
+
55
+ # hardware/system
56
+ "device": "cpu",
57
+ "dtype": "float32",
58
+
59
+ # LLM
60
+ "max_context_size": 4096,
61
+ "number_of_threads": 8 ,
62
+ "verbose_warnings": False,
63
+ "messages_to_keep_in_context": 4,
64
+ "max_tokens_per_response": 256,
65
+ "temperature": 0.7 ,
66
+ "top_p": 0.9,
67
+ "top_k": 50,
68
+ "messages": [
69
+ {"role": "system", "content": """You are Jenbot, an expert, helpful, and diligent assistant. You provide the user with accurate answers to their queries. You are polite, friendly, and a little sarcastic."""},
70
+ {"role": "user", "content": """Hi, who are you?"""},
71
+ ]
72
+ }
73
+ ```
74
+
75
+ ### Run the Generation
76
+
77
+ To run the main workflow:
78
+
79
+ ```py
80
+ from textjenerator.models import registry
81
+ from config import config # or load your config however you prefer
82
+
83
+ text_generator = registry.get_model_class(config) # Choose which model you want from the registry
84
+ response = text_generator.generate_text() # create pipeline and run it
85
+ print(response)
86
+ ```
87
+
88
+ You can also do these steps separately if you want to keep the model in memory. In this case you can pass a new config, typically this will be to include the cumulative chat history, but you could also change other settings on the fly e.g.:
89
+
90
+ * Reduce temperature for a coding question
91
+ * Increase temperature for a creative writing request
92
+ * Increase max_tokens_per_response if you expect a longer reply
93
+
94
+ ```py
95
+ text_generator = registry.get_model_class(config)
96
+ text_generator.create_pipeline()
97
+ response = text_generator.run_pipeline()
98
+ print(response)
99
+
100
+ # or pass another, doesn't have to be the whole thing:
101
+ new_config = {
102
+ "messages": [
103
+ {"role": "system", "content": """You are Jenbot, an expert, helpful, and diligent assistant. You provide the user with accurate answers to their queries. You are polite, friendly, and a little sarcastic."""},
104
+ {"role": "user", "content": """Hi, who are you?"""},
105
+ {"role": "assistant", "content": """You must be wondering who I am. Well, let me introduce myself: I'm Jenbot, your friendly AI assistant. I'm here to help answer any questions you may have, provide information on a wide range of topics, and even offer a dash of sarcasm when the situation calls for it."""},
106
+ {"role": "user", "content": """What are your thoughts on the hard problem of consciousness?"""},
107
+ ]
108
+ }
109
+ response = text_generator.run_pipeline(new_config)
110
+ print(response)
111
+ ```
112
+
@@ -0,0 +1,94 @@
1
+ # Text Jenerator
2
+
3
+ A Unified Python Interface for Modular Text Generation.
4
+
5
+ Text Jenerator is a framework designed to provide a consistent, abstract interface for deploying and interacting with various Large Language Models (LLMs). By separating the generic configuration and workflow from the model-specific implementation, it enables you to easily switch between different model backends—such as local GGUF models (via Llama-CPP), Hugging Face Transformers, or proprietary APIs (e.g., OpenAI, Gemini), with no code changes besides the config you pass.
6
+
7
+ ## Features
8
+
9
+ * **Modular Architecture**: Uses Abstract Base Classes (ABC) to enforce a clean separation between the model details, meaning you only need to instantiate the TextGenerator class and call `generate_text()`, no matter which model you're using.
10
+ * **Automatic Setup**: Handles generic tasks like device detection (CPU/CUDA) and data type selection (bfloat16, float16, float32) in the base class.
11
+ * **Plug-and-Play Models**: Easily integrate new models by creating concrete child classes that implement two simple abstract methods (create_pipeline and run_pipeline).
12
+ * **Model Registry**: Uses a decorator (@register_model) to automatically track and load available generation backends.
13
+
14
+ ## Installation
15
+
16
+ ```sh
17
+ pip install textjenerator
18
+ ```
19
+
20
+ The core package has minimal dependencies. Model-specific implementations (e.g., for Llama-CPP or Transformers) require their respective libraries.
21
+
22
+ ## Models
23
+
24
+ Text Jenerator is BYOM. You can put the local model path or a HuggingFace ID in your config.
25
+
26
+ ## Quick Start & Usage
27
+
28
+ ### Config
29
+
30
+ Your project should include a config file (defaults are at textjenerator.config). A basic config might look like this:
31
+
32
+ ```json
33
+ config = {
34
+ # model
35
+ "model": "llama-cpp",
36
+ "model_path": ".models/Llama-3.2-3B-Instruct-Q4_K_M.gguf",
37
+
38
+ # hardware/system
39
+ "device": "cpu",
40
+ "dtype": "float32",
41
+
42
+ # LLM
43
+ "max_context_size": 4096,
44
+ "number_of_threads": 8 ,
45
+ "verbose_warnings": False,
46
+ "messages_to_keep_in_context": 4,
47
+ "max_tokens_per_response": 256,
48
+ "temperature": 0.7 ,
49
+ "top_p": 0.9,
50
+ "top_k": 50,
51
+ "messages": [
52
+ {"role": "system", "content": """You are Jenbot, an expert, helpful, and diligent assistant. You provide the user with accurate answers to their queries. You are polite, friendly, and a little sarcastic."""},
53
+ {"role": "user", "content": """Hi, who are you?"""},
54
+ ]
55
+ }
56
+ ```
57
+
58
+ ### Run the Generation
59
+
60
+ To run the main workflow:
61
+
62
+ ```py
63
+ from textjenerator.models import registry
64
+ from config import config # or load your config however you prefer
65
+
66
+ text_generator = registry.get_model_class(config) # Choose which model you want from the registry
67
+ response = text_generator.generate_text() # create pipeline and run it
68
+ print(response)
69
+ ```
70
+
71
+ You can also do these steps separately if you want to keep the model in memory. In this case you can pass a new config, typically this will be to include the cumulative chat history, but you could also change other settings on the fly e.g.:
72
+
73
+ * Reduce temperature for a coding question
74
+ * Increase temperature for a creative writing request
75
+ * Increase max_tokens_per_response if you expect a longer reply
76
+
77
+ ```py
78
+ text_generator = registry.get_model_class(config)
79
+ text_generator.create_pipeline()
80
+ response = text_generator.run_pipeline()
81
+ print(response)
82
+
83
+ # or pass another, doesn't have to be the whole thing:
84
+ new_config = {
85
+ "messages": [
86
+ {"role": "system", "content": """You are Jenbot, an expert, helpful, and diligent assistant. You provide the user with accurate answers to their queries. You are polite, friendly, and a little sarcastic."""},
87
+ {"role": "user", "content": """Hi, who are you?"""},
88
+ {"role": "assistant", "content": """You must be wondering who I am. Well, let me introduce myself: I'm Jenbot, your friendly AI assistant. I'm here to help answer any questions you may have, provide information on a wide range of topics, and even offer a dash of sarcasm when the situation calls for it."""},
89
+ {"role": "user", "content": """What are your thoughts on the hard problem of consciousness?"""},
90
+ ]
91
+ }
92
+ response = text_generator.run_pipeline(new_config)
93
+ print(response)
94
+ ```
@@ -0,0 +1,30 @@
1
+ [project]
2
+ name = "textjenerator"
3
+ version = "0.1.0"
4
+ description = "AI text generator"
5
+ authors = [{ name = "Warren Davies"}]
6
+ readme = "README.md"
7
+ license = "MIT"
8
+ homepage = "https://github.com/WarrenDavies/text-jenerator"
9
+ repository = "https://github.com/WarrenDavies/text-jenerator"
10
+ packages = [
11
+ { include = "textjenerator", from = "src" }
12
+ ]
13
+ requires-python = ">=3.10,<3.15"
14
+
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.12",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: OS Independent",
20
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
21
+ ]
22
+
23
+ [tool.poetry.dependencies]
24
+ python = ">=3.10,<3.15"
25
+ llama-cpp-python = ">=0.3.16,<0.4.0"
26
+ torch = "^2.9.1"
27
+
28
+ [build-system]
29
+ requires = ["poetry-core>=1.0.0"]
30
+ build-backend = "poetry.core.masonry.api"
@@ -0,0 +1,23 @@
1
+ config = {
2
+ # model
3
+ "model": "llama-cpp",
4
+ "model_path": ".models/Llama-3.2-3B-Instruct-Q4_K_M.gguf",
5
+
6
+ # hardware/system
7
+ "device": "cpu",
8
+ "dtype": "float32",
9
+ "number_of_threads": 8,
10
+ "n_gpu_layers": -1,
11
+
12
+ # LLM
13
+ "verbose_warnings": False,
14
+ "max_context_size": 4096,
15
+ "max_tokens_per_response": 256,
16
+ "temperature": .9 ,
17
+ "top_p": 0.9,
18
+ "top_k": 50,
19
+ "messages": [
20
+ {"role": "system", "content": """You are Jenbot, an expert, helpful, and diligent assistant. You provide the user with accurate answers to their queries. You are polite, friendly, and a little sarcastic."""},
21
+ {"role": "user", "content": """Hi, who are you?"""},
22
+ ]
23
+ }
@@ -0,0 +1,129 @@
1
+ import copy
2
+
3
+ from abc import ABC, abstractmethod
4
+ import torch
5
+
6
+ from textjenerator.config import config
7
+
8
+
9
+ class TextGenerator(ABC):
10
+ """
11
+ Abstract base class for text generation. This class handles the generic configuration and execution flow and manages device (CPU/CUDA) /data type (e.g., bfloat16) setup.
12
+
13
+ Subclasses must implement create_pipeline() and run_pipeline()
14
+
15
+ Attributes:
16
+ config (dict): Configuration dictionary containing model parameters, paths, and settings.
17
+ DTYPES_MAP (dict): A mapping from string names (e.g., "bfloat16") to torch.dtype objects.
18
+ Add: pipe (Any): The initialized model pipeline (to be set by subclasses).
19
+ Add: response (str/Any): The generated text or model output (to be set by subclasses).
20
+ """
21
+
22
+ def __init__(self, config = config):
23
+ """
24
+ Initializes the object with a config.
25
+
26
+ Args:
27
+ config (dict): A dictionary containing configuration parameters. Default config comes from textjenerator.config
28
+ """
29
+ self.config = config
30
+ self.pipe = None
31
+ self.response = None
32
+ self.dtype = None
33
+ self.device = None
34
+ self.DTYPES_MAP = {
35
+ "bfloat16": torch.bfloat16,
36
+ "float16": torch.float16,
37
+ "float32": torch.float32,
38
+ }
39
+ self.detect_device_and_dtype()
40
+
41
+
42
+ def detect_device_and_dtype(self):
43
+ """
44
+ If 'device' or 'dtype' in config are set to "detect", this method attempts
45
+ to choose the optimal settings based on hardware availability (e.g., CUDA).
46
+ This method modifies self.device and self.dtype based on hardware availability and configuration settings.
47
+ """
48
+ if self.config["device"] == "detect":
49
+ self.set_device()
50
+ else:
51
+ self.device = self.config["device"]
52
+
53
+ self.set_dtype()
54
+
55
+
56
+ def set_device(self):
57
+ """
58
+ Sets the computation device based on CUDA availability.
59
+
60
+ Sets `self.device` to 'cuda' if available, otherwise defaults to 'cpu'.
61
+ """
62
+ if torch.cuda.is_available():
63
+ self.device = "cuda"
64
+ else:
65
+ self.device = "cpu"
66
+
67
+
68
+ def set_dtype(self):
69
+ """
70
+ Sets the torch data type based on the device and configuration.
71
+
72
+ If config['dtype'] is "detect":
73
+ - Sets to torch.bfloat16 if device is 'cuda'.
74
+ - Sets to torch.float32 otherwise.
75
+ Otherwise, maps the string config to the actual torch.dtype object in self.DTYPES_MAP.
76
+ """
77
+ if self.config["dtype"] == "detect":
78
+ if self.device == "cuda":
79
+ self.dtype = torch.bfloat16
80
+ self.config["dtype"] = "bfloat16"
81
+ else:
82
+ self.dtype = torch.float32
83
+ self.config["dtype"] = "float32"
84
+ return
85
+
86
+ self.dtype = self.DTYPES_MAP[self.config["dtype"]]
87
+
88
+
89
+ def merge_config(self, config):
90
+ merged_config = merged_config = copy.deepcopy(self.config)
91
+ merged_config.update(config)
92
+
93
+ return merged_config
94
+
95
+
96
+ @abstractmethod
97
+ def create_pipeline(self):
98
+ """
99
+ Abstract method to initialize the model pipeline.
100
+
101
+ Subclasses must implement this to load the specific model and tokenizer/pipeline object, assigning it to self.pipe (e.g., a Hugging Face Pipeline object).
102
+ """
103
+ pass
104
+
105
+
106
+ @abstractmethod
107
+ def run_pipeline(self):
108
+ """
109
+ Abstract method to execute the model pipeline.
110
+
111
+ Subclasses must implement this to execute the model using self.pipe and store the final generated text in self.response (str).
112
+ """
113
+ pass
114
+
115
+
116
+ def generate_text(self):
117
+ """
118
+ Generates text by orchestrating the pipeline creation and execution. This is the primary public method for text generation.
119
+
120
+ Steps:
121
+ 1. Creates the pipeline.
122
+ 2. Runs the pipeline implementation.
123
+
124
+ Returns: str: The generated text, which is the value of self.response after the pipeline runs.
125
+ """
126
+ self.create_pipeline()
127
+ self.run_pipeline()
128
+ return self.response
129
+
@@ -0,0 +1,7 @@
1
+ from textjenerator.models import registry
2
+ from textjenerator.config import config
3
+
4
+ text_generator = registry.get_model_class(config)
5
+ response = text_generator.generate_text()
6
+ print(response)
7
+
@@ -0,0 +1,30 @@
1
+ from textjenerator.models import registry
2
+ from textjenerator.config import config
3
+
4
+
5
+ text_generator = registry.get_model_class(config)
6
+
7
+ # You can call create_pipeline() and run_pipeline() separately
8
+ # This keeps the model in memory making the response time faster.
9
+ # You can pass another config on each call the run_pipeline()
10
+ # e.g., to add new messages in a conversation, or change settings
11
+ # such as lowering temperature for a coding task.
12
+ text_generator.create_pipeline()
13
+
14
+ # Now you can call run_pipeline, which without arguments will
15
+ # generate based on the config passed when creating the object
16
+ response = text_generator.run_pipeline()
17
+ print(response)
18
+
19
+ # or pass another, doesn't have to be the whole thing:
20
+ new_config = {
21
+ "max_tokens_per_response": 512,
22
+ "messages": [
23
+ {"role": "system", "content": """You are Jenbot, an expert, helpful, and diligent assistant. You provide the user with accurate answers to their queries. You are polite, friendly, and a little sarcastic."""},
24
+ {"role": "user", "content": """Hi, who are you?"""},
25
+ {"role": "assistant", "content": """You must be wondering who I am. Well, let me introduce myself: I'm Jenbot, your friendly AI assistant. I'm here to help answer any questions you may have, provide information on a wide range of topics, and even offer a dash of sarcasm when the situation calls for it."""},
26
+ {"role": "user", "content": """What are your thoughts on the hard problem of consciousness?"""},
27
+ ]
28
+ }
29
+ response = text_generator.run_pipeline(new_config)
30
+ print(response)
@@ -0,0 +1,5 @@
1
+ import pkgutil
2
+ import importlib
3
+
4
+ for module in pkgutil.iter_modules(__path__):
5
+ importlib.import_module(f"{__name__}.{module.name}")
@@ -0,0 +1,95 @@
1
+ from llama_cpp import Llama
2
+
3
+ from textjenerator.models.registry import register_model
4
+ from textjenerator.core.text_generator import TextGenerator
5
+
6
+
7
+ @register_model("llama-cpp")
8
+ class LlamaCPP(TextGenerator):
9
+ """
10
+ Concrete implementation of TextGenerator using the llama-cpp-python library
11
+ for running GGUF-formatted LLMs (e.g., Llama 3, Mistral) efficiently on CPU.
12
+
13
+ It implements the core abstract methods:
14
+ 1. create_pipeline: Initializes the llama_cpp.Llama object.
15
+ 2. run_pipeline: Executes text generation via the create_chat_completion API.
16
+ """
17
+
18
+ def __init__(self, config):
19
+ """
20
+ Initializes the text generator.
21
+
22
+ Args:
23
+ config (dict): Configuration dictionary. Must include standard TextGenerator
24
+ keys plus model-specific keys.
25
+ llm (llama_cpp.Llama | None): The initialized Llama CPP model object.
26
+ """
27
+ super().__init__(config)
28
+ self.llm = None
29
+
30
+
31
+ def create_pipeline(self):
32
+ """
33
+ Loads the pipeline using llama_cpp.Llama and applies configurations.
34
+
35
+ Note that llama-cpp-python handles its own hardware acceleration, so the device
36
+ config (cpu/CUDA) in the parent class is not used here.
37
+
38
+ Requires the following keys in self.config:
39
+ * model_path (str): Path to the GGUF model file.
40
+ * max_context_size (int): The context window size (n_ctx).
41
+ * number_of_threads (int): The number of threads to use (n_threads).
42
+ * verbose_warnings (bool): Enable/disable verbose warnings.
43
+ * n_gpu_layers (int): Number of layers to offload to the GPU (optional, defaults to 0).
44
+
45
+ Raises:
46
+ KeyError: If specific config keys (like model_path) are missing.
47
+ """
48
+ self.llm = Llama(
49
+ model_path=self.config["model_path"],
50
+ n_ctx=self.config["max_context_size"],
51
+ n_threads=self.config["number_of_threads"],
52
+ verbose=self.config["verbose_warnings"],
53
+ n_gpu_layers=self.config["n_gpu_layers"]
54
+ )
55
+
56
+
57
+ def run_pipeline(self, config=None):
58
+ """
59
+ Executes the inference using the chat completion API.
60
+
61
+ Args:
62
+ config (dict | None): A dictionary containing temporary overrides for
63
+ this specific inference call (e.g., the next message).
64
+ These settings are merged with and override the permanent
65
+ self.config for this run only. If None, self.config is used directly.
66
+
67
+ Requires the following keys in onfig:
68
+ * messages (list): The chat history in the expected Llama format.
69
+ * max_tokens_per_response (int): Max new tokens to generate.
70
+ * temperature (float): Sampling temperature.
71
+ * top_p (float): Top-p sampling value.
72
+ * top_k (int): Top-k sampling value.
73
+
74
+ The final generated text is stored in self.response and returned.
75
+ """
76
+ if config:
77
+ config = self.merge_config(config)
78
+ else:
79
+ config = self.config
80
+
81
+ output = self.llm.create_chat_completion(
82
+ messages=config["messages"],
83
+ max_tokens=config["max_tokens_per_response"],
84
+ temperature=config["temperature"],
85
+ top_p=config["top_p"],
86
+ top_k=config["top_k"],
87
+ )
88
+ choice = output["choices"][0]["message"]
89
+ output_text = (choice.get("content") or "").strip()
90
+ if not output_text:
91
+ output_text = "[No response generated.]"
92
+ self.response = output_text
93
+
94
+ return self.response
95
+
@@ -0,0 +1,54 @@
1
+ """
2
+ Implements a simple decorator-based model registry for dynamically managing
3
+ different model classes.
4
+
5
+ This system allows concrete subclasses to register themselves
6
+ using a specific string key, enabling the application to instantiate the
7
+ correct class based on a configuration setting without explicit imports.
8
+ """
9
+
10
+ MODEL_REGISTRY = {}
11
+
12
+ def register_model(name):
13
+ """
14
+ A decorator factory used to register a subclass.
15
+
16
+ The decorated class is stored in the global MODEL_REGISTRY dictionary
17
+ under the provided `name`.
18
+
19
+ Args:
20
+ name (str): The string key used to reference the model class
21
+ in the configuration.
22
+
23
+ Returns:
24
+ Callable: A decorator function that takes a class and registers it.
25
+ """
26
+ def decorator(cls):
27
+ MODEL_REGISTRY[name] = cls
28
+ return cls
29
+ return decorator
30
+
31
+
32
+ def get_model_class(config):
33
+ """
34
+ Retrieves and instantiates the correct class based on the
35
+ configuration dictionary.
36
+
37
+ It looks up the class in MODEL_REGISTRY using the key found in
38
+ `config['model']`.
39
+
40
+ Args:
41
+ config (dict): The configuration dictionary, which must contain a
42
+ 'model' key corresponding to a registered model name.
43
+
44
+ Returns:
45
+ BaseClass: An instantiated object of the registered generator class.
46
+
47
+ Raises:
48
+ KeyError: If the value of `config['model']` is not found in the
49
+ MODEL_REGISTRY.
50
+ """
51
+ ModelClass = MODEL_REGISTRY[config["model"]]
52
+ model_object = ModelClass(config)
53
+
54
+ return model_object