Skip to content

Commit b1da169

Browse files
authored
Merge pull request #127 from vectorlessflow/dev
Dev
2 parents 5947508 + 333711a commit b1da169

7 files changed

Lines changed: 24 additions & 86 deletions

File tree

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ members = [
1717
resolver = "2"
1818

1919
[workspace.package]
20-
version = "0.1.14"
20+
version = "0.1.15"
2121
description = "Knowing by reasoning, not vectors."
2222
edition = "2024"
2323
authors = ["zTgx <beautifularea@gmail.com>"]
@@ -97,7 +97,7 @@ rand = "0.8"
9797
bm25 = { version = "2.3.2", features = ["parallelism"] }
9898

9999
# Python bindings
100-
pyo3 = { version = "0.28", features = ["extension-module"] }
100+
pyo3 = { version = "0.28", features = ["extension-module", "abi3-py311"] }
101101
pyo3-async-runtimes = { version = "0.28", features = ["tokio-runtime"] }
102102

103103
# Dev dependencies

HISTORY.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# HISTORY
22

3+
## 0.1.15 (2026-04-29)
4+
5+
- **Python bindings**: enabled PyO3 abi3 for cross-version compatibility
6+
- Single wheel now supports Python 3.11, 3.12, and 3.13+
7+
- Simplified exception type using `create_exception!` macro
8+
39
## 0.1.14 (2026-04-29)
410

511
- Homepage redesign with product showcase and improved UX

crates/vectorless-py/src/document.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use std::sync::Arc;
77

88
use pyo3::prelude::*;
9+
use pyo3::exceptions::PyRuntimeError;
910
use pyo3_async_runtimes::tokio::future_into_py;
1011
use tokio::sync::Mutex;
1112

@@ -15,8 +16,6 @@ use vectorless_primitives::{
1516
SectionCardInfo, SectionSummaryInfo, SimilarResult, TocEntry, TopicEntryInfo, WordCount,
1617
};
1718

18-
use super::error::VectorlessError;
19-
2019
// =========================================================================
2120
// PyDocumentInfo (existing — returned by compile)
2221
// =========================================================================
@@ -126,7 +125,7 @@ fn id_to_str(id: u64) -> String {
126125
}
127126

128127
fn to_py_err(e: impl std::fmt::Display) -> PyErr {
129-
PyErr::from(VectorlessError::new(e.to_string(), "navigation"))
128+
PyRuntimeError::new_err(e.to_string())
130129
}
131130

132131
#[pymethods]
@@ -382,10 +381,10 @@ impl PyDocument {
382381
let num = node_id
383382
.strip_prefix('n')
384383
.ok_or_else(|| {
385-
VectorlessError::new("NodeId must start with 'n'".to_string(), "navigation")
384+
PyRuntimeError::new_err("NodeId must start with 'n'")
386385
})?
387386
.parse::<u64>()
388-
.map_err(|_| VectorlessError::new("Invalid NodeId".to_string(), "navigation"))?;
387+
.map_err(|_| PyRuntimeError::new_err("Invalid NodeId"))?;
389388
let nav = nav.lock().await;
390389
Ok(nav
391390
.chains_for(num)
@@ -403,10 +402,10 @@ impl PyDocument {
403402
let num = node_id
404403
.strip_prefix('n')
405404
.ok_or_else(|| {
406-
VectorlessError::new("NodeId must start with 'n'".to_string(), "navigation")
405+
PyRuntimeError::new_err("NodeId must start with 'n'")
407406
})?
408407
.parse::<u64>()
409-
.map_err(|_| VectorlessError::new("Invalid NodeId".to_string(), "navigation"))?;
408+
.map_err(|_| PyRuntimeError::new_err("Invalid NodeId"))?;
410409
let nav = nav.lock().await;
411410
Ok(nav
412411
.overlaps_for(num)
@@ -424,10 +423,10 @@ impl PyDocument {
424423
let num = node_id
425424
.strip_prefix('n')
426425
.ok_or_else(|| {
427-
VectorlessError::new("NodeId must start with 'n'".to_string(), "navigation")
426+
PyRuntimeError::new_err("NodeId must start with 'n'")
428427
})?
429428
.parse::<u64>()
430-
.map_err(|_| VectorlessError::new("Invalid NodeId".to_string(), "navigation"))?;
429+
.map_err(|_| PyRuntimeError::new_err("Invalid NodeId"))?;
431430
let nav = nav.lock().await;
432431
Ok(nav.evidence_score_for(num).await.map(PyEvidenceScore::from))
433432
})

crates/vectorless-py/src/engine.rs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
//! Engine Python wrapper — async compile/forget/list_documents.
55
66
use pyo3::prelude::*;
7+
use pyo3::exceptions::PyRuntimeError;
78
use pyo3_async_runtimes::tokio::future_into_py;
89
use std::sync::Arc;
910
use tokio::runtime::Runtime;
1011

1112
use ::vectorless_engine::{Engine, EngineBuilder, IngestInput, RawNodeInput};
1213

1314
use super::document::{PyDocument, PyDocumentInfo};
14-
use super::error::VectorlessError;
1515
use super::error::to_py_err;
1616
use super::graph::PyDocumentGraph;
1717
use super::metrics::PyMetricsReport;
@@ -73,10 +73,7 @@ async fn run_load_document(engine: Arc<Engine>, doc_id: String) -> PyResult<PyDo
7373
let navigator = vectorless_primitives::DocumentNavigator::new(d);
7474
Ok(PyDocument::from_navigator(navigator))
7575
}
76-
None => Err(PyErr::from(VectorlessError::new(
77-
format!("Document not found: {doc_id}"),
78-
"navigation",
79-
))),
76+
None => Err(PyRuntimeError::new_err(format!("Document not found: {doc_id}"))),
8077
}
8178
}
8279

@@ -144,10 +141,7 @@ impl PyEngine {
144141
config: Option<PyRef<super::config::PyConfig>>,
145142
) -> PyResult<Self> {
146143
let rt = Runtime::new().map_err(|e| {
147-
PyErr::from(VectorlessError::new(
148-
format!("Failed to create tokio runtime: {}", e),
149-
"config",
150-
))
144+
PyRuntimeError::new_err(format!("Failed to create tokio runtime: {}", e))
151145
})?;
152146

153147
let rust_config = config.map(|c| c.inner.clone());
@@ -173,10 +167,7 @@ impl PyEngine {
173167
});
174168

175169
let engine = engine.map_err(|e| {
176-
PyErr::from(VectorlessError::new(
177-
format!("Failed to create engine: {}", e),
178-
"config",
179-
))
170+
PyRuntimeError::new_err(format!("Failed to create engine: {}", e))
180171
})?;
181172

182173
Ok(Self {

crates/vectorless-py/src/error.rs

Lines changed: 2 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -3,69 +3,12 @@
33

44
//! Python exception types and error conversion.
55
6-
use pyo3::exceptions::PyException;
6+
use pyo3::exceptions::PyRuntimeError;
77
use pyo3::prelude::*;
88

99
use ::vectorless_engine::Error as RustError;
1010

11-
/// Python exception for vectorless errors.
12-
#[pyclass(extends = PyException, subclass, skip_from_py_object)]
13-
pub struct VectorlessError {
14-
message: String,
15-
kind: String,
16-
}
17-
18-
#[pymethods]
19-
impl VectorlessError {
20-
#[new]
21-
fn new_py(message: String, kind: String) -> Self {
22-
Self { message, kind }
23-
}
24-
25-
#[getter]
26-
fn message(&self) -> &str {
27-
&self.message
28-
}
29-
30-
#[getter]
31-
fn kind(&self) -> &str {
32-
&self.kind
33-
}
34-
35-
fn __str__(&self) -> &str {
36-
&self.message
37-
}
38-
39-
fn __repr__(&self) -> String {
40-
format!("VectorlessError('{}', kind='{}')", self.message, self.kind)
41-
}
42-
}
43-
44-
impl VectorlessError {
45-
pub fn new(message: String, kind: &str) -> Self {
46-
Self {
47-
message,
48-
kind: kind.to_string(),
49-
}
50-
}
51-
}
52-
53-
impl From<VectorlessError> for PyErr {
54-
fn from(err: VectorlessError) -> PyErr {
55-
PyErr::new::<VectorlessError, _>((err.message, err.kind))
56-
}
57-
}
58-
5911
/// Convert vectorless errors to Python exceptions.
6012
pub fn to_py_err(e: RustError) -> PyErr {
61-
let message = e.to_string();
62-
let kind = match &e {
63-
RustError::DocumentNotFound(_) => "not_found",
64-
RustError::Parse(_) => "parse",
65-
RustError::Config(_) => "config",
66-
RustError::Workspace(_) => "workspace",
67-
RustError::Llm(_) => "llm",
68-
_ => "unknown",
69-
};
70-
VectorlessError::new(message, kind).into()
13+
PyRuntimeError::new_err(e.to_string())
7114
}

crates/vectorless-py/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ use document::{
2020
PyTocEntry, PyTopicEntry, PyWordCount,
2121
};
2222
use engine::PyEngine;
23-
use error::VectorlessError;
2423
use graph::{PyDocumentGraph, PyDocumentGraphNode, PyEdgeEvidence, PyGraphEdge, PyWeightedKeyword};
2524
use metrics::{PyLlmMetricsReport, PyMetricsReport, PyRetrievalMetricsReport};
2625

@@ -39,7 +38,7 @@ fn _vectorless(m: &Bound<'_, PyModule>) -> PyResult<()> {
3938
.init();
4039
});
4140

42-
m.add_class::<VectorlessError>()?;
41+
// VectorlessError is just PyRuntimeError, no need to register
4342
m.add_class::<PyEngine>()?;
4443
m.add_class::<PyDocument>()?;
4544
m.add_class::<PyDocumentInfo>()?;

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ Repository = "https://github.com/vectorlessflow/vectorless"
7070
python-source = "."
7171
module-name = "vectorless._vectorless"
7272
manifest-path = "crates/vectorless-py/Cargo.toml"
73-
features = ["pyo3/extension-module"]
73+
features = ["pyo3/extension-module", "pyo3/abi3-py311"]
7474

7575
[tool.pytest.ini_options]
7676
asyncio_mode = "auto"

0 commit comments

Comments
 (0)