mirror of
https://github.com/lancedb/lancedb.git
synced 2026-08-25 07:28:39 +00:00
a588208de6
## Problem The canonical Function wire values and typed remote Job contract do not yet provide a Python authoring surface or catalog client, so users cannot package a scalar callable, register it, or reopen the exact immutable Function version. ## Behavior This adds scalar-only `@udf` authoring with deterministic annotation or explicit Arrow schema validation, content-addressed Python artifacts, and an internal scalar-to-Arrow-batch adapter descriptor. Registration payloads model non-secret environment values and secret names only. Remote connections can submit `create_function_async` and receive a typed `Job<FunctionVersion>`, then reopen that exact version by name and version ID. Synchronous connections can call `create_function` to submit and wait for the immutable version in one operation. Local Function catalog operations return a stable `NotSupported` error. Shared Rust/Python golden payloads and mocked catalog responses freeze the request, typed terminal result, and exact lookup contract. ## Validation - Rust formatting, remote check, clippy, and focused LDB-1/LDB-2 tests - Python formatting, lint, and focused LDB-1/LDB-2 tests - Python API documentation build
82 lines
2.7 KiB
Rust
82 lines
2.7 KiB
Rust
// SPDX-License-Identifier: Apache-2.0
|
|
// SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
|
|
|
use std::fs;
|
|
use std::path::PathBuf;
|
|
|
|
use lancedb::Error;
|
|
use lancedb::function::FunctionRegistrationRequest;
|
|
use serde_json::Value;
|
|
|
|
fn fixture(name: &str) -> String {
|
|
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
|
.join("tests/fixtures/first_class_functions/v1")
|
|
.join(name);
|
|
fs::read_to_string(path).expect("fixture must be readable")
|
|
}
|
|
|
|
fn assert_no_secret_values(value: &Value) {
|
|
match value {
|
|
Value::Object(values) => {
|
|
for (key, value) in values {
|
|
assert!(
|
|
!matches!(
|
|
key.as_str(),
|
|
"secret_value" | "secret_values" | "resolved_secret" | "resolved_secrets"
|
|
),
|
|
"registration requests must not model resolved secret material"
|
|
);
|
|
assert_no_secret_values(value);
|
|
}
|
|
}
|
|
Value::Array(values) => values.iter().for_each(assert_no_secret_values),
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn registration_request_matches_shared_canonical_golden() {
|
|
let request = FunctionRegistrationRequest::from_json(&fixture(
|
|
"remote_function_registration_request.json",
|
|
))
|
|
.expect("registration request");
|
|
assert_eq!(request.name, "normalize_score");
|
|
assert_eq!(request.artifact.adapter.kind, "scalar_to_arrow_batch");
|
|
assert_eq!(request.required_secrets, ["API_TOKEN"]);
|
|
assert_eq!(
|
|
request.to_canonical_json().expect("canonical request"),
|
|
fixture("remote_function_registration_request.canonical.json").trim()
|
|
);
|
|
|
|
let value: Value =
|
|
serde_json::from_str(&request.to_canonical_json().expect("canonical request"))
|
|
.expect("request JSON");
|
|
assert_no_secret_values(&value);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn local_function_catalog_operations_return_stable_not_supported() {
|
|
let directory = tempfile::tempdir().unwrap();
|
|
let connection = lancedb::connect(directory.path().to_str().unwrap())
|
|
.execute()
|
|
.await
|
|
.unwrap();
|
|
let request = FunctionRegistrationRequest::from_json(&fixture(
|
|
"remote_function_registration_request.json",
|
|
))
|
|
.unwrap();
|
|
|
|
let create_error = connection.create_function_async(request).await.unwrap_err();
|
|
let lookup_error = connection
|
|
.get_function("normalize_score", "fv_exact")
|
|
.await
|
|
.unwrap_err();
|
|
for error in [create_error, lookup_error] {
|
|
assert!(matches!(
|
|
error,
|
|
Error::NotSupported { message }
|
|
if message == "Function catalog operations are not supported by this database"
|
|
));
|
|
}
|
|
}
|