fix: Prefer GNU linux builds in install scripts for duckdb support, and gate duckdb support on linux hosts that are MUSL until MUSL support is added
This commit is contained in:
@@ -62,7 +62,13 @@ if ($os -eq 'windows') {
|
|||||||
else { $candidates += 'coyote-aarch64-apple-darwin.tar.gz' }
|
else { $candidates += 'coyote-aarch64-apple-darwin.tar.gz' }
|
||||||
} elseif ($os -eq 'linux') {
|
} elseif ($os -eq 'linux') {
|
||||||
if ($arch -eq 'x86_64') {
|
if ($arch -eq 'x86_64') {
|
||||||
$candidates += 'coyote-x86_64-unknown-linux-gnu.tar.gz'
|
# Mirror install_coyote.sh: only offer the gnu build when glibc is detected.
|
||||||
|
$libc = 'musl'
|
||||||
|
try { getconf GNU_LIBC_VERSION *> $null; if ($LASTEXITCODE -eq 0) { $libc = 'gnu' } } catch { }
|
||||||
|
try { if ((ldd --version 2>&1 | Out-String) -imatch 'glibc') { $libc = 'gnu' } } catch { }
|
||||||
|
if ($libc -eq 'gnu') {
|
||||||
|
$candidates += 'coyote-x86_64-unknown-linux-gnu.tar.gz'
|
||||||
|
}
|
||||||
$candidates += 'coyote-x86_64-unknown-linux-musl.tar.gz'
|
$candidates += 'coyote-x86_64-unknown-linux-musl.tar.gz'
|
||||||
} else {
|
} else {
|
||||||
$candidates += 'coyote-aarch64-unknown-linux-musl.tar.gz'
|
$candidates += 'coyote-aarch64-unknown-linux-musl.tar.gz'
|
||||||
|
|||||||
@@ -68,9 +68,32 @@ fn normalize_version(requested: Option<String>) -> Option<String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn host_has_glibc() -> bool {
|
||||||
|
if process::Command::new("getconf")
|
||||||
|
.arg("GNU_LIBC_VERSION")
|
||||||
|
.output()
|
||||||
|
.is_ok_and(|out| out.status.success())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
process::Command::new("ldd")
|
||||||
|
.arg("--version")
|
||||||
|
.output()
|
||||||
|
.is_ok_and(|out| {
|
||||||
|
let combined = format!(
|
||||||
|
"{}{}",
|
||||||
|
String::from_utf8_lossy(&out.stdout),
|
||||||
|
String::from_utf8_lossy(&out.stderr)
|
||||||
|
);
|
||||||
|
combined.to_lowercase().contains("glibc")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn preferred_update_target() -> Option<&'static str> {
|
fn preferred_update_target() -> Option<&'static str> {
|
||||||
match (env::consts::OS, env::consts::ARCH) {
|
match (env::consts::OS, env::consts::ARCH) {
|
||||||
|
("linux", "x86_64") if host_has_glibc() => Some("x86_64-unknown-linux-gnu"),
|
||||||
("linux", "x86_64") => Some("x86_64-unknown-linux-musl"),
|
("linux", "x86_64") => Some("x86_64-unknown-linux-musl"),
|
||||||
|
// No aarch64 gnu asset is published; musl is the only option.
|
||||||
("linux", "aarch64") => Some("aarch64-unknown-linux-musl"),
|
("linux", "aarch64") => Some("aarch64-unknown-linux-musl"),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1869,6 +1869,12 @@ fn select_embedding_model(models: &[&Model]) -> Result<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn select_rag_driver() -> Result<String> {
|
pub(crate) fn select_rag_driver() -> Result<String> {
|
||||||
|
// Statically linked musl builds cannot dlopen DuckDB extensions, so duckdb is not offered there.
|
||||||
|
#[cfg(target_env = "musl")]
|
||||||
|
let options = vec![
|
||||||
|
"yaml — portable, in-memory HNSW; usable from several Coyote processes at once (default)",
|
||||||
|
];
|
||||||
|
#[cfg(not(target_env = "musl"))]
|
||||||
let options = vec![
|
let options = vec![
|
||||||
"yaml — portable, in-memory HNSW; usable from several Coyote processes at once (default)",
|
"yaml — portable, in-memory HNSW; usable from several Coyote processes at once (default)",
|
||||||
"duckdb — persistent on-disk store; vectors and content survive restarts; HNSW approximate search.",
|
"duckdb — persistent on-disk store; vectors and content survive restarts; HNSW approximate search.",
|
||||||
|
|||||||
@@ -85,6 +85,21 @@ pub struct DuckDbProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DuckDbProvider {
|
impl DuckDbProvider {
|
||||||
|
/// Fail fast on musl builds, before any filesystem or DuckDB work. Coyote's musl
|
||||||
|
/// builds are statically linked, and a statically linked binary cannot dlopen the
|
||||||
|
/// `vss`/`fts` extension shared objects, so this driver can never work there.
|
||||||
|
fn reject_musl() -> Result<()> {
|
||||||
|
#[cfg(target_env = "musl")]
|
||||||
|
bail!(
|
||||||
|
"The duckdb RAG driver is unavailable in this build of coyote: musl builds \
|
||||||
|
are statically linked, and a statically linked binary cannot load DuckDB \
|
||||||
|
extensions like `vss`/`fts` (dlopen is unsupported). Use the `qdrant` RAG \
|
||||||
|
driver instead, or a gnu-libc build of coyote."
|
||||||
|
);
|
||||||
|
#[cfg(not(target_env = "musl"))]
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Open (or create) the DuckDB file. `dim` is the embedding vector dimension,
|
/// Open (or create) the DuckDB file. `dim` is the embedding vector dimension,
|
||||||
/// supplied by the caller who knows the model.
|
/// supplied by the caller who knows the model.
|
||||||
///
|
///
|
||||||
@@ -95,6 +110,7 @@ impl DuckDbProvider {
|
|||||||
/// created or initialized here, or lazily through `ensure_writable` on the rebuild
|
/// created or initialized here, or lazily through `ensure_writable` on the rebuild
|
||||||
/// path.
|
/// path.
|
||||||
pub fn open(db_path: &Path, dim: usize) -> Result<Self> {
|
pub fn open(db_path: &Path, dim: usize) -> Result<Self> {
|
||||||
|
Self::reject_musl()?;
|
||||||
let (conn, writable) = Self::open_for_workload(db_path, dim)?;
|
let (conn, writable) = Self::open_for_workload(db_path, dim)?;
|
||||||
// A reopened file may already carry a live FTS index from a previous session,
|
// A reopened file may already carry a live FTS index from a previous session,
|
||||||
// in which case keyword search works immediately.
|
// in which case keyword search works immediately.
|
||||||
@@ -173,6 +189,7 @@ impl DuckDbProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn introspect_dim(db_path: &Path) -> Result<Option<usize>> {
|
pub fn introspect_dim(db_path: &Path) -> Result<Option<usize>> {
|
||||||
|
Self::reject_musl()?;
|
||||||
if !db_path.exists() {
|
if !db_path.exists() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
@@ -819,7 +836,9 @@ impl RagProvider for DuckDbProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
// Not compiled for musl: `open` bails there (statically linked binaries cannot dlopen
|
||||||
|
// the extension shared objects), so every test in this module would fail at setup.
|
||||||
|
#[cfg(all(test, not(target_env = "musl")))]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::rag::provider::RagProvider;
|
use crate::rag::provider::RagProvider;
|
||||||
|
|||||||
Reference in New Issue
Block a user