Skip to content

Commit eea51c9

Browse files
committed
Add ik_llama.cpp fork support and unique build naming
- Add repository_source field to LlamaVersion table to track fork type - Support building from ik_llama.cpp fork alongside main llama.cpp - Implement unique naming for source builds: source-{commit}-{timestamp} or source-{commit}-{suffix} - Allow multiple builds of same commit to coexist with different names - Add repository source dropdown and build name suffix field to build dialog - Add split_mode='graph' option for ik_llama.cpp support - Display repository source indicator in version list - Update API endpoints to handle repository_source and version_suffix parameters
1 parent b11a604 commit eea51c9

8 files changed

Lines changed: 191 additions & 32 deletions

File tree

backend/database.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class LlamaVersion(Base):
8080
installed_at = Column(DateTime, default=datetime.utcnow)
8181
is_active = Column(Boolean, default=False) # Changed from is_default to is_active
8282
build_config = Column(JSON) # Store BuildConfig as JSON
83+
repository_source = Column(String, default="llama.cpp") # "llama.cpp" or "ik_llama.cpp"
8384

8485

8586
class RunningInstance(Base):
@@ -138,6 +139,10 @@ async def init_db():
138139
ensure_pipeline_tag_column()
139140
except Exception as exc:
140141
logger.warning(f"Failed to ensure models.pipeline_tag column: {exc}")
142+
try:
143+
ensure_repository_source_column()
144+
except Exception as exc:
145+
logger.warning(f"Failed to ensure repository_source column: {exc}")
141146

142147
# Migrate existing models to populate base_model_name
143148
migrate_existing_models()
@@ -284,4 +289,17 @@ def ensure_pipeline_tag_column():
284289

285290
with engine.connect() as connection:
286291
connection.execute(text("ALTER TABLE models ADD COLUMN pipeline_tag VARCHAR"))
287-
logger.info("Added pipeline_tag column to models table")
292+
logger.info("Added pipeline_tag column to models table")
293+
294+
295+
def ensure_repository_source_column():
296+
"""Ensure the llama_versions table has the repository_source column."""
297+
inspector = inspect(engine)
298+
columns = [column["name"] for column in inspector.get_columns("llama_versions")]
299+
if "repository_source" in columns:
300+
return
301+
302+
with engine.connect() as connection:
303+
connection.execute(text("ALTER TABLE llama_versions ADD COLUMN repository_source VARCHAR"))
304+
connection.execute(text("UPDATE llama_versions SET repository_source = 'llama.cpp' WHERE repository_source IS NULL"))
305+
logger.info("Added repository_source column to llama_versions table")

backend/llama_manager.py

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,15 @@ def normalize(self):
7272

7373

7474
class LlamaManager:
75+
# Repository URLs
76+
LLAMA_CPP_REPO = "https://github.com/ggerganov/llama.cpp.git"
77+
IK_LLAMA_CPP_REPO = "https://github.com/ikawrakow/ik_llama.cpp.git"
78+
79+
REPOSITORY_SOURCES = {
80+
"llama.cpp": LLAMA_CPP_REPO,
81+
"ik_llama.cpp": IK_LLAMA_CPP_REPO
82+
}
83+
7584
def __init__(self):
7685
self.llama_dir = "data/llama-cpp"
7786
os.makedirs(self.llama_dir, exist_ok=True)
@@ -712,29 +721,39 @@ async def install_release(
712721
logger.error(f"Failed to send error to WebSocket: {ws_error}")
713722
raise Exception(f"Failed to install release {tag_name}: {e}")
714723

715-
async def build_source(self, commit_sha: str, patches: List[str] = None, build_config: BuildConfig = None, websocket_manager=None, task_id: str = None) -> str:
724+
async def build_source(self, commit_sha: str, patches: List[str] = None, build_config: BuildConfig = None, websocket_manager=None, task_id: str = None, repository_url: str = None, version_name: str = None) -> str:
716725
"""Build llama.cpp from source following official documentation - simplified approach"""
717726
try:
727+
# Use default repository if not specified
728+
if repository_url is None:
729+
repository_url = self.LLAMA_CPP_REPO
730+
731+
# Determine repository source name for logging
732+
repo_source_name = "llama.cpp"
733+
for source_name, repo_url in self.REPOSITORY_SOURCES.items():
734+
if repo_url == repository_url:
735+
repo_source_name = source_name
736+
break
737+
718738
# Send initial progress
719739
if websocket_manager and task_id:
720740
await websocket_manager.send_build_progress(
721741
task_id=task_id,
722742
stage="init",
723743
progress=0,
724-
message="Starting simplified build process...",
725-
log_lines=[f"Building llama.cpp from {commit_sha}"]
744+
message=f"Starting simplified build process for {repo_source_name}...",
745+
log_lines=[f"Building {repo_source_name} from {commit_sha}"]
726746
)
727747

728-
# Create version directory
729-
version_name = f"source-{commit_sha[:8]}"
730-
version_dir = os.path.join(self.llama_dir, version_name)
748+
# Use provided version_name or generate default (shouldn't happen, but fallback)
749+
if version_name is None:
750+
version_name = f"source-{commit_sha[:8]}"
751+
logger.warning(f"No version_name provided, using default: {version_name}")
731752

732-
# Clean up existing directory
733-
if os.path.exists(version_dir):
734-
logger.info(f"Cleaning up existing directory: {version_dir}")
735-
shutil.rmtree(version_dir, ignore_errors=True)
736-
time.sleep(1)
753+
version_dir = os.path.join(self.llama_dir, version_name)
737754

755+
# Don't clean up existing directory - let API handle uniqueness check
756+
# This allows multiple builds of the same commit with different names
738757
os.makedirs(version_dir, exist_ok=True)
739758

740759
# Stage 1: Clone repository (simplified)
@@ -743,16 +762,16 @@ async def build_source(self, commit_sha: str, patches: List[str] = None, build_c
743762
task_id=task_id,
744763
stage="clone",
745764
progress=20,
746-
message="Cloning llama.cpp repository...",
747-
log_lines=["Cloning repository..."]
765+
message=f"Cloning {repo_source_name} repository...",
766+
log_lines=[f"Cloning {repo_source_name} repository..."]
748767
)
749768

750769
clone_dir = os.path.join(version_dir, "llama.cpp")
751770

752771
# Simple git clone with timeout
753772
try:
754773
clone_process = await asyncio.create_subprocess_exec(
755-
"git", "clone", "https://github.com/ggerganov/llama.cpp.git", clone_dir,
774+
"git", "clone", repository_url, clone_dir,
756775
stdout=asyncio.subprocess.PIPE,
757776
stderr=asyncio.subprocess.PIPE
758777
)

backend/routes/llama_versions.py

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ async def list_llama_versions(db: Session = Depends(get_db)):
3636
"patches": json.loads(version.patches) if version.patches else [],
3737
"installed_at": version.installed_at,
3838
"is_active": version.is_active,
39-
"build_config": version.build_config
39+
"build_config": version.build_config,
40+
"repository_source": version.repository_source or "llama.cpp"
4041
}
4142
for version in versions
4243
]
@@ -258,16 +259,30 @@ async def build_source(
258259
commit_sha = request.get("commit_sha")
259260
patches = request.get("patches", [])
260261
build_config_dict = request.get("build_config")
262+
repository_source = request.get("repository_source", "llama.cpp")
263+
version_suffix = request.get("version_suffix")
261264

262265
if not commit_sha:
263266
raise HTTPException(status_code=400, detail="commit_sha is required")
264267

265-
version_name = f"source-{commit_sha[:8]}"
268+
# Generate unique version name
269+
commit_short = commit_sha[:8]
270+
if version_suffix:
271+
version_name = f"source-{commit_short}-{version_suffix}"
272+
else:
273+
# Use timestamp for unique naming
274+
timestamp = int(time.time())
275+
version_name = f"source-{commit_short}-{timestamp}"
266276

267-
# Check if version already exists
277+
# Check if version already exists (still check to prevent accidental duplicates)
268278
existing = db.query(LlamaVersion).filter(LlamaVersion.version == version_name).first()
269279
if existing:
270-
raise HTTPException(status_code=400, detail="Version already installed")
280+
raise HTTPException(status_code=400, detail=f"Version '{version_name}' already installed")
281+
282+
# Get repository URL from source name
283+
repository_url = llama_manager.REPOSITORY_SOURCES.get(repository_source)
284+
if not repository_url:
285+
raise HTTPException(status_code=400, detail=f"Unknown repository source: {repository_source}")
271286

272287
# Parse build_config if provided
273288
build_config = None
@@ -284,6 +299,8 @@ async def build_source(
284299
patches,
285300
build_config,
286301
version_name,
302+
repository_source,
303+
repository_url,
287304
websocket_manager,
288305
task_id
289306
)
@@ -292,26 +309,38 @@ async def build_source(
292309
"message": f"Building from source {commit_sha[:8]}",
293310
"task_id": task_id,
294311
"status": "started",
295-
"progress": 0
312+
"progress": 0,
313+
"version_name": version_name,
314+
"repository_source": repository_source
296315
}
297316
except Exception as e:
298317
raise HTTPException(status_code=500, detail=str(e))
299318

300319

301-
async def build_source_task(commit_sha: str, patches: List[str], build_config: BuildConfig, version_name: str, websocket_manager=None, task_id: str = None):
320+
async def build_source_task(commit_sha: str, patches: List[str], build_config: BuildConfig, version_name: str, repository_source: str, repository_url: str, websocket_manager=None, task_id: str = None):
302321
"""Background task to build from source with WebSocket progress"""
303322
# Create a new database session for the background task
304323
from backend.database import SessionLocal
305324
from dataclasses import asdict
306325
db = SessionLocal()
307326

308327
try:
309-
binary_path = await llama_manager.build_source(commit_sha, patches, build_config, websocket_manager, task_id)
328+
binary_path = await llama_manager.build_source(
329+
commit_sha,
330+
patches,
331+
build_config,
332+
websocket_manager,
333+
task_id,
334+
repository_url=repository_url,
335+
version_name=version_name
336+
)
310337

311338
# Save to database with build_config
312339
build_config_dict = None
313340
if build_config:
314341
build_config_dict = asdict(build_config)
342+
# Add repository_source to build_config for completeness
343+
build_config_dict["repository_source"] = repository_source
315344

316345
version = LlamaVersion(
317346
version=version_name,
@@ -320,6 +349,7 @@ async def build_source_task(commit_sha: str, patches: List[str], build_config: B
320349
source_commit=commit_sha,
321350
patches=json.dumps(patches),
322351
build_config=build_config_dict,
352+
repository_source=repository_source,
323353
installed_at=datetime.utcnow()
324354
)
325355
db.add(version)
@@ -329,7 +359,7 @@ async def build_source_task(commit_sha: str, patches: List[str], build_config: B
329359
if websocket_manager:
330360
await websocket_manager.send_notification(
331361
title="Build Complete",
332-
message=f"Successfully built llama.cpp from source {commit_sha[:8]}",
362+
message=f"Successfully built {repository_source} from source {commit_sha[:8]}",
333363
type="success"
334364
)
335365

frontend/src/components/config/EssentialSettingsSection.vue

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
</template>
6363
</ConfigField>
6464
<ConfigField label="CPU Threads" help-text="CPU threads for computation">
65-
<template #input>
65+
<template #input>gi
6666
<SliderInput
6767
v-model="config.threads"
6868
:min="1"
@@ -127,10 +127,13 @@ const gpuOptions = computed(() => {
127127
})
128128
129129
// Split mode options
130+
// Note: "graph" mode is supported by ik_llama.cpp fork
131+
// Backend validation will handle if binary doesn't support it
130132
const splitModeOptions = [
131133
{ label: 'None (single GPU)', value: 'none' },
132134
{ label: 'Layer (default)', value: 'layer' },
133-
{ label: 'Row', value: 'row' }
135+
{ label: 'Row', value: 'row' },
136+
{ label: 'Graph (ik_llama.cpp)', value: 'graph' }
134137
]
135138
</script>
136139

frontend/src/components/system/LlamaCppManager/BuildDialog.vue

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,19 @@
1212
<div class="build-form">
1313
<div class="dialog-section">
1414
<h4 class="section-title">Source Information</h4>
15+
<div class="form-row">
16+
<div class="form-field">
17+
<label>Repository Source *</label>
18+
<Dropdown
19+
v-model="buildForm.repositorySource"
20+
:options="repositorySourceOptions"
21+
optionLabel="label"
22+
optionValue="value"
23+
placeholder="Select repository"
24+
/>
25+
<small>Choose the repository to build from</small>
26+
</div>
27+
</div>
1528
<div class="form-field full-width">
1629
<label>Commit SHA or Branch *</label>
1730
<InputText
@@ -21,6 +34,20 @@
2134
<small>Default: master (latest stable)</small>
2235
</div>
2336

37+
<div class="form-field full-width">
38+
<label>Build Name Suffix (Optional)</label>
39+
<InputText
40+
v-model="buildForm.versionSuffix"
41+
placeholder="e.g., test-build, production"
42+
/>
43+
<small>Custom suffix for version name. If empty, timestamp will be used.</small>
44+
</div>
45+
46+
<div v-if="previewVersionName" class="version-preview">
47+
<i class="pi pi-info-circle"></i>
48+
<span>Version name: <strong>{{ previewVersionName }}</strong></span>
49+
</div>
50+
2451
<div class="form-field full-width">
2552
<label>Patches (Optional)</label>
2653
<Textarea
@@ -250,7 +277,7 @@
250277
</template>
251278

252279
<script setup>
253-
import { ref, watch, onMounted } from 'vue'
280+
import { ref, watch, computed, onMounted } from 'vue'
254281
import { useSystemStore } from '@/stores/system'
255282
import { toast } from 'vue3-toastify'
256283
import Button from 'primevue/button'
@@ -284,7 +311,9 @@ const systemStore = useSystemStore()
284311
const building = ref(false)
285312
286313
const buildForm = ref({
314+
repositorySource: 'llama.cpp',
287315
commitSha: 'master',
316+
versionSuffix: '',
288317
patches: '',
289318
buildType: 'Release',
290319
enableCuda: false,
@@ -307,6 +336,20 @@ const buildForm = ref({
307336
cxxflags: ''
308337
})
309338
339+
const repositorySourceOptions = [
340+
{ label: 'llama.cpp (Official)', value: 'llama.cpp' },
341+
{ label: 'ik_llama.cpp (Fork)', value: 'ik_llama.cpp' }
342+
]
343+
344+
const previewVersionName = computed(() => {
345+
if (!buildForm.value.commitSha) return null
346+
const commitShort = buildForm.value.commitSha.substring(0, 8)
347+
if (buildForm.value.versionSuffix) {
348+
return `source-${commitShort}-${buildForm.value.versionSuffix}`
349+
}
350+
return `source-${commitShort}-{timestamp}`
351+
})
352+
310353
const getCapabilityClass = (capability) => {
311354
if (!capability) return 'text-gray-500'
312355
return capability.available ? 'text-green-500' : 'text-gray-500'
@@ -343,8 +386,20 @@ const handleBuild = async () => {
343386
cxxflags: buildForm.value.cxxflags || ''
344387
}
345388
346-
await systemStore.buildSource(buildForm.value.commitSha, patches, buildConfig)
347-
emit('build', { commitSha: buildForm.value.commitSha, patches, buildConfig })
389+
await systemStore.buildSource(
390+
buildForm.value.commitSha,
391+
patches,
392+
buildConfig,
393+
buildForm.value.repositorySource,
394+
buildForm.value.versionSuffix || null
395+
)
396+
emit('build', {
397+
commitSha: buildForm.value.commitSha,
398+
patches,
399+
buildConfig,
400+
repositorySource: buildForm.value.repositorySource,
401+
versionSuffix: buildForm.value.versionSuffix
402+
})
348403
emit('update:visible', false)
349404
toast.success('Build started successfully')
350405
} catch (error) {
@@ -583,5 +638,27 @@ watch(
583638
color: var(--text-secondary);
584639
font-size: 0.8rem;
585640
}
641+
642+
.version-preview {
643+
display: flex;
644+
align-items: center;
645+
gap: 0.5rem;
646+
padding: 0.75rem;
647+
background: var(--gradient-surface);
648+
border-radius: var(--radius-md);
649+
border: 1px solid var(--border-primary);
650+
margin-top: 0.5rem;
651+
font-size: 0.875rem;
652+
color: var(--text-primary);
653+
}
654+
655+
.version-preview i {
656+
color: var(--accent-blue);
657+
}
658+
659+
.version-preview strong {
660+
color: var(--accent-cyan);
661+
font-family: monospace;
662+
}
586663
</style>
587664

0 commit comments

Comments
 (0)