diff --git a/vgi-example-worker/src/accumulate/mod.rs b/vgi-example-worker/src/accumulate/mod.rs index 50f8ec1..d72678a 100644 --- a/vgi-example-worker/src/accumulate/mod.rs +++ b/vgi-example-worker/src/accumulate/mod.rs @@ -675,6 +675,7 @@ pub fn catalog() -> CatalogModel { schemas: vec![CatSchema { name: "main".to_string(), comment: Some("Stateful row accumulation functions".to_string()), + tags: Vec::new(), views: Vec::new(), macros: Vec::new(), tables: Vec::new(), diff --git a/vgi-example-worker/src/attach_options.rs b/vgi-example-worker/src/attach_options.rs index f60921e..c696147 100644 --- a/vgi-example-worker/src/attach_options.rs +++ b/vgi-example-worker/src/attach_options.rs @@ -219,6 +219,7 @@ pub fn catalog() -> vgi::catalog::CatalogModel { schemas: vec![vgi::catalog::CatSchema { name: "main".to_string(), comment: None, + tags: Vec::new(), views: Vec::new(), macros: Vec::new(), tables: Vec::new(), diff --git a/vgi-example-worker/src/catalog_def.rs b/vgi-example-worker/src/catalog_def.rs index 041d4af..c772b56 100644 --- a/vgi-example-worker/src/catalog_def.rs +++ b/vgi-example-worker/src/catalog_def.rs @@ -958,10 +958,12 @@ pub fn versioned() -> CatalogModel { .to_string(), ), tags: Vec::new(), + source_url: None, supports_time_travel: false, schemas: vec![CatSchema { name: "main".to_string(), comment: None, + tags: Vec::new(), views: Vec::new(), macros: Vec::new(), tables: Vec::new(), @@ -977,6 +979,7 @@ pub fn versioned_tables() -> CatalogModel { let main = |tables: Vec| CatSchema { name: "main".to_string(), comment: None, + tags: Vec::new(), views: Vec::new(), macros: Vec::new(), tables, @@ -1020,6 +1023,7 @@ pub fn versioned_tables() -> CatalogModel { version_schemas.insert("3.0.0".to_string(), vec![main(vec![plants.clone()])]); CatalogModel { name: "versioned_tables".to_string(), + source_url: None, implementation_version: Some("11.0.0".to_string()), data_version_spec: Some(">=1.0.0,<4.0.0".to_string()), supported_data_versions: vec![ @@ -1060,6 +1064,7 @@ pub fn build_by_name(name: &str) -> CatalogModel { pub fn build() -> CatalogModel { CatalogModel { name: "example".to_string(), + source_url: None, implementation_version: None, data_version_spec: None, supported_data_versions: Vec::new(), @@ -1079,6 +1084,7 @@ pub fn build() -> CatalogModel { CatSchema { name: "main".to_string(), comment: Some("Example functions for testing VGI".to_string()), + tags: Vec::new(), views: vec![ CatView { comment: Some("First 10 integers".to_string()), @@ -1111,6 +1117,7 @@ pub fn build() -> CatalogModel { CatSchema { name: "data".to_string(), comment: Some("Example tables backed by functions".to_string()), + tags: Vec::new(), views: vec![CatView { column_comments: kv(&[("value", "Single-digit value 0..9")]), ..view("small_numbers", "SELECT * FROM numbers WHERE value < 10") diff --git a/vgi-example-worker/src/narrow_bind/mod.rs b/vgi-example-worker/src/narrow_bind/mod.rs index cf446ff..cc2f317 100644 --- a/vgi-example-worker/src/narrow_bind/mod.rs +++ b/vgi-example-worker/src/narrow_bind/mod.rs @@ -159,6 +159,7 @@ pub fn catalog() -> CatalogModel { schemas: vec![CatSchema { name: "main".to_string(), comment: Some("narrow-bind reproducer catalog".to_string()), + tags: Vec::new(), views: Vec::new(), macros: Vec::new(), tables: vec![ diff --git a/vgi/src/catalog.rs b/vgi/src/catalog.rs index c22a162..c50628a 100644 --- a/vgi/src/catalog.rs +++ b/vgi/src/catalog.rs @@ -107,7 +107,7 @@ pub fn serialize_catalog_info(model: &CatalogModel) -> Result> { let rel_field = Arc::new(Field::new("item", DataType::Struct(rel_fields), true)); let releases = one_empty_list(rel_field, rel_values); - let source_url = Arc::new(StringArray::from(vec![Option::::None])) as ArrayRef; + let source_url = Arc::new(StringArray::from(vec![model.source_url.clone()])) as ArrayRef; let schema = Arc::new(Schema::new(vec![ Field::new("name", DataType::Utf8, false), @@ -478,6 +478,9 @@ pub struct CatalogModel { pub comment: Option, /// Database-level tags (surfaced via `duckdb_databases().tags`). pub tags: Vec<(String, String)>, + /// Provenance URL (repo / docs / dataset homepage) advertised via + /// `catalog_catalogs().source_url` so consumers can verify provenance. + pub source_url: Option, /// Whether the catalog supports time-travel (`AT`) queries. pub supports_time_travel: bool, /// Worker software version (singular per worker). `None` = no opinion. @@ -595,10 +598,13 @@ pub fn resolve_version_npm( Err(unsupported()) } -#[derive(Clone)] +#[derive(Default, Clone)] pub struct CatSchema { pub name: String, pub comment: Option, + /// Schema-level tags (surfaced via `duckdb_schemas().tags`), e.g. + /// `vgi.description_llm` / `vgi.description_md`. + pub tags: Vec<(String, String)>, pub views: Vec, pub macros: Vec, pub tables: Vec, diff --git a/vgi/src/dispatch.rs b/vgi/src/dispatch.rs index 3225788..428ce7e 100644 --- a/vgi/src/dispatch.rs +++ b/vgi/src/dispatch.rs @@ -1131,6 +1131,9 @@ impl Dispatcher { cat.name.as_bytes().to_vec() }; let mut si = catalog::schema_info(name, comment, &attach); + // Schema-level tags (e.g. vgi.description_llm / vgi.description_md) come + // from the declarative CatSchema, surfaced via duckdb_schemas().tags. + si.tags = cat.schema(name).map(|s| s.tags.clone()).unwrap_or_default(); // Object counts come from the (primary) worker-global function // registries, so only advertise them for the primary, non-version-shaped // catalog. Version-shaped catalogs vary their object set per attach, and