# 🧩 Integrating Local LLMs into **ShinkaEvolve** ## 🧠 Overview The original **ShinkaEvolve** code does **not** include built-in support for running **local LLMs**. To enable this functionality, parts of the codebase can be modified to integrate locally hosted models. --- ## πŸ—οΈ Code Organization **ShinkaEvolve** uses a **modular architecture** that supports multiple **LLM providers**. The relevant code for LLM interaction is located in the **`LLM/`** folder, which manages all model communications. ShinkaEvolve distinguishes between two LLM types: * **Regular LLMs** * **Embedding LLMs** --- ## βš™οΈ Adding a Regular LLM To add support for a **regular LLM**, follow these steps. They will show an example of adding support for gpt-oss models running with unsloth, which provides an API compatible with OpenAI API (v1/completions). This LLM can then be specified in the configuration variables: ```yaml llm_models: meta_llm_models: ``` --- ### πŸ”§ Step 1: Modify the Client The file **`client.py`** is responsible for creating clients that interact with LLMs. Each client instance is later used to query a specific model. To add a local model, introduce a new client configuration. The API URL is extracted from the model name, which follows this format: ``` local-gptoss-unsloth-url ``` #### Example ```python elif "local-gptoss-unsloth" in model_name: # Extract URL from model name pattern = r"https?://" match = re.search(pattern, model_name) if match: start_index = match.start() url = model_name[start_index:] else: raise ValueError(f"Invalid URL in model name: {model_name}") # Create OpenAI-compatible client client = openai.OpenAI( api_key="filler", base_url=url ) # Structured output mode (if required) if structured_output: client = instructor.from_openai( client, mode=instructor.Mode.JSON, ) ``` --- ### πŸ“ Step 2: Create the Local Query Function Inside the **`models/`** folder, create a new subfolder to store the query functions for your local models: ``` LLM/models/local/ ``` > Don’t forget to include an empty `__init__.py` file. This folder should contain a **custom query function** for the local model. I called my file local_gptoss_unsloth.py. It should follow the same structure as other functions in `LLM/models/`, but with small adjustments. #### My Key Adjustments * Replace `max_output_tokens` with **`max_tokens`** to match the local API. * Extract additional response metadata such as: * `total_tokens` * `thinking_tokens` (if your model includes reasoning traces) This function is later imported and registered in **`query.py`**. --- ### 🧩 Step 3: Update `__init__.py` Configure **`__init__.py`** to include and expose the new local query function, so it can be imported elsewhere. ``` from .local.local_gptoss_unsloth import query_local_gptoss_unsloth # ADDED THIS LINE from .result import QueryResult __all__ = [ "query_anthropic", "query_openai", "query_deepseek", "query_gemini", "query_local_gptoss_unsloth", # ADDED THIS LINE "QueryResult", ] ``` --- ### πŸ“¬ Step 4: Update `query.py` Import and register the new local query function in query.py. #### Imports ```python from .models import ( query_anthropic, query_openai, query_deepseek, query_gemini, query_local_gptoss_unsloth, # ADDED THIS LINE QueryResult, ) ``` #### Model Selection Logic ```python elif "local-gptoss-unsloth" in model_name: # ADDED THIS LINE query_fn = query_local_gptoss_unsloth ``` --- ### 🧠 Step 5: Other Observations The file **`query.py`** also defines functions such as: * `sample_model_kwargs` * `sample_batch_kwargs` However, these are **not referenced anywhere else** in the repository, so no modifications are required here for now. --- ### βœ… Summary | Step | File | Change | Description | | ---- | -------------------------------------------- | -------------------- | -------------------------------------------------------- | | 1 | `client.py` | Add new client block | Create OpenAI-compatible client for local LLM | | 2 | `models/local/query_local_gptoss_unsloth.py` | New function | Query local model, adjust tokens, extract reasoning info | | 3 | `__init__.py` | Add import | Expose new query function | | 4 | `query.py` | Register model | Add conditional for local LLM | | 5 | β€” | Review only | Ignored unused functions | --- ## 🧬 Adding a Local Embedding Model For embedding models, you can use **Ollama**, which follows the **OpenAI API** format. The only relevant file is **`embedding.py`**. ### Code Addition ```python elif model_name.startswith("local-"): # Pattern: local-(model-name)-(http or https url) match = re.match(r"local-(.+?)-(https?://.+)", model_name) if match: model_to_use = match.group(1) url = match.group(2) else: raise ValueError(f"Invalid local model format: {model_name}") client = openai.OpenAI( base_url=url, api_key="filler" ) ``` #### Notes * Compatible with **any Ollama model**. * The model name must follow this convention: ``` local-model-name-url ``` * The code extracts both `model-name` and `url`, and uses them to query Ollama. --- ### Query Logic The existing line in **`embedding.py`** remains unchanged: ```python response = self.client.embeddings.create( model=self.model, input=code, encoding_format="float" ) ``` For local embedding models, `self.model` corresponds to the extracted model name. The only addition to the **Embedding Client** class: ```python elif self.model_name.startswith("local-"): cost = 0.0 ``` --- ## πŸš€ Result ShinkaEvolve can now connect to **locally hosted LLMs** and **embedding models** through **OpenAI-compatible APIs**. This setup supports **Ollama** and other frameworks such as **gpt-oss** under **Unsloth**. If your model has different requirements, follow the same pattern with a distinct model identifier and your own custom logic.