File size: 6,525 Bytes
1556404 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# 🧩 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.
|