Skip to content

Commit f0bc867

Browse files
authored
Add API in Python SDK to add and reference auth entries. (#287)
* Move Python `_dump_engine_object` to `convert.py`. * Add API in Python SDK to add and reference auth entries.
1 parent 029175b commit f0bc867

File tree

5 files changed

+70
-31
lines changed

5 files changed

+70
-31
lines changed

python/cocoindex/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77
from .flow import update_all_flows, FlowLiveUpdater, FlowLiveUpdaterOptions
88
from .llm import LlmSpec, LlmApiType
99
from .vector import VectorSimilarityMetric
10+
from .auth_registry import AuthEntryReference, add_auth_entry, ref_auth_entry
1011
from .lib import *
11-
from ._engine import OpArgSchema
12+
from ._engine import OpArgSchema

python/cocoindex/auth_registry.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"""
2+
Auth registry is used to register and reference auth entries.
3+
"""
4+
5+
from dataclasses import dataclass
6+
7+
from . import _engine
8+
from .convert import dump_engine_object
9+
10+
@dataclass
11+
class AuthEntryReference:
12+
"""Reference an auth entry by its key."""
13+
key: str
14+
15+
def add_auth_entry(key: str, value) -> AuthEntryReference:
16+
"""Add an auth entry to the registry. Returns its reference."""
17+
_engine.add_auth_entry(key, dump_engine_object(value))
18+
return AuthEntryReference(key)
19+
20+
def ref_auth_entry(key: str) -> AuthEntryReference:
21+
"""Reference an auth entry by its key."""
22+
return AuthEntryReference(key)

python/cocoindex/convert.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22
Utilities to convert between Python and engine values.
33
"""
44
import dataclasses
5+
import datetime
56
import inspect
67
import uuid
78

8-
from typing import Any, Callable
9-
from .typing import analyze_type_info, COLLECTION_TYPES
9+
from enum import Enum
10+
from typing import Any, Callable, get_origin
11+
from .typing import analyze_type_info, encode_enriched_type, COLLECTION_TYPES
1012

1113
def to_engine_value(value: Any) -> Any:
1214
"""Convert a Python value to an engine value."""
@@ -100,3 +102,24 @@ def make_closure_for_value(name: str, param: inspect.Parameter) -> Callable[[lis
100102

101103
return lambda values: dst_dataclass_type(
102104
*(converter(values) for converter in field_value_converters))
105+
106+
def dump_engine_object(v: Any) -> Any:
107+
"""Recursively dump an object for engine. Engine side uses `Pythonized` to catch."""
108+
if v is None:
109+
return None
110+
elif isinstance(v, type) or get_origin(v) is not None:
111+
return encode_enriched_type(v)
112+
elif isinstance(v, Enum):
113+
return v.value
114+
elif isinstance(v, datetime.timedelta):
115+
total_secs = v.total_seconds()
116+
secs = int(total_secs)
117+
nanos = int((total_secs - secs) * 1e9)
118+
return {'secs': secs, 'nanos': nanos}
119+
elif hasattr(v, '__dict__'):
120+
return {k: dump_engine_object(v) for k, v in v.__dict__.items()}
121+
elif isinstance(v, (list, tuple)):
122+
return [dump_engine_object(item) for item in v]
123+
elif isinstance(v, dict):
124+
return {k: dump_engine_object(v) for k, v in v.items()}
125+
return v

python/cocoindex/flow.py

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@
88
import re
99
import inspect
1010
import datetime
11-
from typing import Any, Callable, Sequence, TypeVar, get_origin
11+
12+
from typing import Any, Callable, Sequence, TypeVar
1213
from threading import Lock
1314
from enum import Enum
1415
from dataclasses import dataclass
1516

1617
from . import _engine
1718
from . import vector
1819
from . import op
20+
from .convert import dump_engine_object
1921
from .typing import encode_enriched_type
2022

2123
class _NameBuilder:
@@ -64,27 +66,6 @@ def _create_data_slice(
6466
def _spec_kind(spec: Any) -> str:
6567
return spec.__class__.__name__
6668

67-
def _dump_engine_object(v: Any) -> Any:
68-
"""Recursively dump an object for engine. Engine side uses `Pythonzized` to catch."""
69-
if v is None:
70-
return None
71-
elif isinstance(v, type) or get_origin(v) is not None:
72-
return encode_enriched_type(v)
73-
elif isinstance(v, Enum):
74-
return v.value
75-
elif isinstance(v, datetime.timedelta):
76-
total_secs = v.total_seconds()
77-
secs = int(total_secs)
78-
nanos = int((total_secs - secs) * 1e9)
79-
return {'secs': secs, 'nanos': nanos}
80-
elif hasattr(v, '__dict__'):
81-
return {k: _dump_engine_object(v) for k, v in v.__dict__.items()}
82-
elif isinstance(v, (list, tuple)):
83-
return [_dump_engine_object(item) for item in v]
84-
elif isinstance(v, dict):
85-
return {k: _dump_engine_object(v) for k, v in v.items()}
86-
return v
87-
8869
T = TypeVar('T')
8970

9071
class _DataSliceState:
@@ -176,6 +157,7 @@ def transform(self, fn_spec: op.FunctionSpec, *args, **kwargs) -> DataSlice:
176157
"""
177158
Apply a function to the data slice.
178159
"""
160+
transform_args: list[tuple[Any, str | None]]
179161
transform_args = [(self._state.engine_data_slice, None)]
180162
transform_args += [(self._state.flow_builder_state.get_data_slice(v), None) for v in args]
181163
transform_args += [(self._state.flow_builder_state.get_data_slice(v), k)
@@ -187,7 +169,7 @@ def transform(self, fn_spec: op.FunctionSpec, *args, **kwargs) -> DataSlice:
187169
lambda target_scope, name:
188170
flow_builder_state.engine_flow_builder.transform(
189171
_spec_kind(fn_spec),
190-
_dump_engine_object(fn_spec),
172+
dump_engine_object(fn_spec),
191173
transform_args,
192174
target_scope,
193175
flow_builder_state.field_name_builder.build_name(
@@ -298,7 +280,7 @@ def export(self, name: str, target_spec: op.StorageSpec, /, *,
298280
{"field_name": field_name, "metric": metric.value}
299281
for field_name, metric in vector_index]
300282
self._flow_builder_state.engine_flow_builder.export(
301-
name, _spec_kind(target_spec), _dump_engine_object(target_spec),
283+
name, _spec_kind(target_spec), dump_engine_object(target_spec),
302284
index_options, self._engine_data_collector, setup_by_user)
303285

304286

@@ -357,11 +339,11 @@ def add_source(self, spec: op.SourceSpec, /, *,
357339
self._state,
358340
lambda target_scope, name: self._state.engine_flow_builder.add_source(
359341
_spec_kind(spec),
360-
_dump_engine_object(spec),
342+
dump_engine_object(spec),
361343
target_scope,
362344
self._state.field_name_builder.build_name(
363345
name, prefix=_to_snake_case(_spec_kind(spec))+'_'),
364-
_dump_engine_object(_SourceRefreshOptions(refresh_interval=refresh_interval)),
346+
dump_engine_object(_SourceRefreshOptions(refresh_interval=refresh_interval)),
365347
),
366348
name
367349
)
@@ -382,7 +364,7 @@ class FlowLiveUpdater:
382364

383365
def __init__(self, fl: Flow, options: FlowLiveUpdaterOptions | None = None):
384366
self._engine_live_updater = _engine.FlowLiveUpdater(
385-
fl._lazy_engine_flow(), _dump_engine_object(options or FlowLiveUpdaterOptions()))
367+
fl._lazy_engine_flow(), dump_engine_object(options or FlowLiveUpdaterOptions()))
386368

387369
def __enter__(self) -> FlowLiveUpdater:
388370
return self
@@ -469,7 +451,7 @@ def evaluate_and_dump(self, options: EvaluateAndDumpOptions):
469451
"""
470452
Evaluate the flow and dump flow outputs to files.
471453
"""
472-
return self._lazy_engine_flow().evaluate_and_dump(_dump_engine_object(options))
454+
return self._lazy_engine_flow().evaluate_and_dump(dump_engine_object(options))
473455

474456
def internal_flow(self) -> _engine.Flow:
475457
"""

src/py/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,16 @@ fn apply_setup_changes(py: Python<'_>, setup_status: &SetupStatusCheck) -> PyRes
321321
})
322322
}
323323

324+
#[pyfunction]
325+
fn add_auth_entry(key: String, value: Pythonized<serde_json::Value>) -> PyResult<()> {
326+
let lib_context = get_lib_context().into_py_result()?;
327+
lib_context
328+
.auth_registry
329+
.add(key, value.into_inner())
330+
.into_py_result()?;
331+
Ok(())
332+
}
333+
324334
/// A Python module implemented in Rust.
325335
#[pymodule]
326336
#[pyo3(name = "_engine")]
@@ -333,6 +343,7 @@ fn cocoindex_engine(m: &Bound<'_, PyModule>) -> PyResult<()> {
333343
m.add_function(wrap_pyfunction!(drop_setup, m)?)?;
334344
m.add_function(wrap_pyfunction!(apply_setup_changes, m)?)?;
335345
m.add_function(wrap_pyfunction!(flow_names_with_setup, m)?)?;
346+
m.add_function(wrap_pyfunction!(add_auth_entry, m)?)?;
336347

337348
m.add_class::<builder::flow_builder::FlowBuilder>()?;
338349
m.add_class::<builder::flow_builder::DataCollector>()?;

0 commit comments

Comments
 (0)