cocoindex 0.1.38__cp313-cp313-macosx_10_12_x86_64.whl → 0.1.40__cp313-cp313-macosx_10_12_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cocoindex/__init__.py +4 -3
- cocoindex/_engine.cpython-313-darwin.so +0 -0
- cocoindex/convert.py +1 -1
- cocoindex/flow.py +130 -29
- cocoindex/op.py +12 -12
- cocoindex/query.py +1 -1
- cocoindex/setting.py +1 -1
- cocoindex/utils.py +9 -0
- {cocoindex-0.1.38.dist-info → cocoindex-0.1.40.dist-info}/METADATA +2 -2
- {cocoindex-0.1.38.dist-info → cocoindex-0.1.40.dist-info}/RECORD +12 -11
- {cocoindex-0.1.38.dist-info → cocoindex-0.1.40.dist-info}/WHEEL +0 -0
- {cocoindex-0.1.38.dist-info → cocoindex-0.1.40.dist-info}/licenses/LICENSE +0 -0
cocoindex/__init__.py
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
"""
|
2
2
|
Cocoindex is a framework for building and running indexing pipelines.
|
3
3
|
"""
|
4
|
-
from . import functions, query, sources, storages, cli
|
5
|
-
from .flow import FlowBuilder, DataScope, DataSlice, Flow, flow_def
|
4
|
+
from . import functions, query, sources, storages, cli, utils
|
5
|
+
from .flow import FlowBuilder, DataScope, DataSlice, Flow, flow_def, transform_flow
|
6
6
|
from .flow import EvaluateAndDumpOptions, GeneratedField
|
7
7
|
from .flow import update_all_flows_async, FlowLiveUpdater, FlowLiveUpdaterOptions
|
8
8
|
from .llm import LlmSpec, LlmApiType
|
9
9
|
from .index import VectorSimilarityMetric, VectorIndexDef, IndexOptions
|
10
10
|
from .auth_registry import AuthEntryReference, add_auth_entry, ref_auth_entry
|
11
11
|
from .lib import *
|
12
|
-
from .setting import
|
12
|
+
from .setting import DatabaseConnectionSpec, Settings, ServerSettings
|
13
|
+
from .setting import get_app_namespace
|
13
14
|
from ._engine import OpArgSchema
|
14
15
|
from .typing import Float32, Float64, LocalDateTime, OffsetDateTime, Range, Vector, Json
|
Binary file
|
cocoindex/convert.py
CHANGED
@@ -44,7 +44,7 @@ def make_engine_value_decoder(
|
|
44
44
|
|
45
45
|
src_type_kind = src_type['kind']
|
46
46
|
|
47
|
-
if dst_annotation is inspect.Parameter.empty:
|
47
|
+
if dst_annotation is None or dst_annotation is inspect.Parameter.empty or dst_annotation is Any:
|
48
48
|
if src_type_kind == 'Struct' or src_type_kind in TABLE_TYPES:
|
49
49
|
raise ValueError(f"Missing type annotation for `{''.join(field_path)}`."
|
50
50
|
f"It's required for {src_type_kind} type.")
|
cocoindex/flow.py
CHANGED
@@ -8,8 +8,9 @@ import asyncio
|
|
8
8
|
import re
|
9
9
|
import inspect
|
10
10
|
import datetime
|
11
|
+
import functools
|
11
12
|
|
12
|
-
from typing import Any, Callable, Sequence, TypeVar
|
13
|
+
from typing import Any, Callable, Sequence, TypeVar, Generic, get_args, get_origin, Type, NamedTuple
|
13
14
|
from threading import Lock
|
14
15
|
from enum import Enum
|
15
16
|
from dataclasses import dataclass
|
@@ -20,7 +21,7 @@ from . import _engine
|
|
20
21
|
from . import index
|
21
22
|
from . import op
|
22
23
|
from . import setting
|
23
|
-
from .convert import dump_engine_object
|
24
|
+
from .convert import dump_engine_object, encode_engine_value, make_engine_value_decoder
|
24
25
|
from .typing import encode_enriched_type
|
25
26
|
from .runtime import execution_context
|
26
27
|
|
@@ -123,7 +124,7 @@ class _DataSliceState:
|
|
123
124
|
# TODO: We'll support this by an identity transformer or "aliasing" in the future.
|
124
125
|
raise ValueError("DataSlice is already attached to a field")
|
125
126
|
|
126
|
-
class DataSlice:
|
127
|
+
class DataSlice(Generic[T]):
|
127
128
|
"""A data slice represents a slice of data in a flow. It's readonly."""
|
128
129
|
|
129
130
|
_state: _DataSliceState
|
@@ -183,11 +184,11 @@ class DataSlice:
|
|
183
184
|
name, prefix=_to_snake_case(_spec_kind(fn_spec))+'_'),
|
184
185
|
))
|
185
186
|
|
186
|
-
def call(self, func: Callable[[DataSlice], T]) -> T:
|
187
|
+
def call(self, func: Callable[[DataSlice], T], *args, **kwargs) -> T:
|
187
188
|
"""
|
188
189
|
Call a function with the data slice.
|
189
190
|
"""
|
190
|
-
return func(self)
|
191
|
+
return func(self, *args, **kwargs)
|
191
192
|
|
192
193
|
def _data_slice_state(data_slice: DataSlice) -> _DataSliceState:
|
193
194
|
return data_slice._state # pylint: disable=protected-access
|
@@ -309,9 +310,8 @@ class _FlowBuilderState:
|
|
309
310
|
engine_flow_builder: _engine.FlowBuilder
|
310
311
|
field_name_builder: _NameBuilder
|
311
312
|
|
312
|
-
def __init__(self,
|
313
|
-
|
314
|
-
self.engine_flow_builder = _engine.FlowBuilder(get_full_flow_name(flow_name))
|
313
|
+
def __init__(self, full_name: str):
|
314
|
+
self.engine_flow_builder = _engine.FlowBuilder(full_name)
|
315
315
|
self.field_name_builder = _NameBuilder()
|
316
316
|
|
317
317
|
def get_data_slice(self, v: Any) -> _engine.DataSlice:
|
@@ -463,9 +463,13 @@ class Flow:
|
|
463
463
|
"""
|
464
464
|
A flow describes an indexing pipeline.
|
465
465
|
"""
|
466
|
+
_name: str
|
467
|
+
_full_name: str
|
466
468
|
_lazy_engine_flow: Callable[[], _engine.Flow]
|
467
469
|
|
468
|
-
def __init__(self, engine_flow_creator: Callable[[], _engine.Flow]):
|
470
|
+
def __init__(self, name: str, full_name: str, engine_flow_creator: Callable[[], _engine.Flow]):
|
471
|
+
self._name = name
|
472
|
+
self._full_name = full_name
|
469
473
|
engine_flow = None
|
470
474
|
lock = Lock()
|
471
475
|
def _lazy_engine_flow() -> _engine.Flow:
|
@@ -496,7 +500,7 @@ class Flow:
|
|
496
500
|
tree.children.append(section_node)
|
497
501
|
return tree
|
498
502
|
|
499
|
-
def _get_spec(self, verbose: bool = False) ->
|
503
|
+
def _get_spec(self, verbose: bool = False) -> _engine.RenderedSpec:
|
500
504
|
return self._lazy_engine_flow().get_spec(output_mode="verbose" if verbose else "concise")
|
501
505
|
|
502
506
|
def _get_schema(self) -> list[tuple[str, str, str]]:
|
@@ -508,12 +512,19 @@ class Flow:
|
|
508
512
|
def __repr__(self):
|
509
513
|
return repr(self._lazy_engine_flow())
|
510
514
|
|
515
|
+
@property
|
516
|
+
def name(self) -> str:
|
517
|
+
"""
|
518
|
+
Get the name of the flow.
|
519
|
+
"""
|
520
|
+
return self._name
|
521
|
+
|
511
522
|
@property
|
512
523
|
def full_name(self) -> str:
|
513
524
|
"""
|
514
525
|
Get the full name of the flow.
|
515
526
|
"""
|
516
|
-
return self.
|
527
|
+
return self._full_name
|
517
528
|
|
518
529
|
def update(self) -> _engine.IndexUpdateInfo:
|
519
530
|
"""
|
@@ -554,14 +565,16 @@ def _create_lazy_flow(name: str | None, fl_def: Callable[[FlowBuilder, DataScope
|
|
554
565
|
Create a flow without really building it yet.
|
555
566
|
The flow will be built the first time when it's really needed.
|
556
567
|
"""
|
568
|
+
flow_name = _flow_name_builder.build_name(name, prefix="_flow_")
|
569
|
+
flow_full_name = get_full_flow_name(flow_name)
|
557
570
|
def _create_engine_flow() -> _engine.Flow:
|
558
|
-
flow_builder_state = _FlowBuilderState(
|
571
|
+
flow_builder_state = _FlowBuilderState(flow_full_name)
|
559
572
|
root_scope = DataScope(
|
560
573
|
flow_builder_state, flow_builder_state.engine_flow_builder.root_scope())
|
561
574
|
fl_def(FlowBuilder(flow_builder_state), root_scope)
|
562
575
|
return flow_builder_state.engine_flow_builder.build_flow(execution_context.event_loop)
|
563
576
|
|
564
|
-
return Flow(_create_engine_flow)
|
577
|
+
return Flow(flow_name, flow_full_name, _create_engine_flow)
|
565
578
|
|
566
579
|
|
567
580
|
_flows_lock = Lock()
|
@@ -642,27 +655,67 @@ async def update_all_flows_async(options: FlowLiveUpdaterOptions) -> dict[str, _
|
|
642
655
|
all_stats = await asyncio.gather(*(_update_flow(name, fl) for (name, fl) in fls.items()))
|
643
656
|
return dict(all_stats)
|
644
657
|
|
645
|
-
|
646
|
-
|
658
|
+
def _get_data_slice_annotation_type(data_slice_type: Type[DataSlice[T]]) -> Type[T] | None:
|
659
|
+
type_args = get_args(data_slice_type)
|
660
|
+
if data_slice_type is DataSlice:
|
661
|
+
return None
|
662
|
+
if get_origin(data_slice_type) != DataSlice or len(type_args) != 1:
|
663
|
+
raise ValueError(f"Expect a DataSlice[T] type, but got {data_slice_type}")
|
664
|
+
return type_args[0]
|
665
|
+
|
666
|
+
_transform_flow_name_builder = _NameBuilder()
|
667
|
+
|
668
|
+
class TransformFlowInfo(NamedTuple):
|
669
|
+
engine_flow: _engine.TransientFlow
|
670
|
+
result_decoder: Callable[[Any], T]
|
671
|
+
|
672
|
+
class TransformFlow(Generic[T]):
|
647
673
|
"""
|
648
674
|
A transient transformation flow that transforms in-memory data.
|
649
675
|
"""
|
650
|
-
|
676
|
+
_flow_fn: Callable[..., DataSlice[T]]
|
677
|
+
_flow_name: str
|
678
|
+
_flow_arg_types: list[Any]
|
679
|
+
_param_names: list[str]
|
680
|
+
|
681
|
+
_lazy_lock: asyncio.Lock
|
682
|
+
_lazy_flow_info: TransformFlowInfo | None = None
|
651
683
|
|
652
684
|
def __init__(
|
653
|
-
self, flow_fn: Callable[..., DataSlice],
|
685
|
+
self, flow_fn: Callable[..., DataSlice[T]],
|
654
686
|
flow_arg_types: Sequence[Any], /, name: str | None = None):
|
687
|
+
self._flow_fn = flow_fn
|
688
|
+
self._flow_name = _transform_flow_name_builder.build_name(name, prefix="_transform_flow_")
|
689
|
+
self._flow_arg_types = list(flow_arg_types)
|
690
|
+
self._lazy_lock = asyncio.Lock()
|
691
|
+
|
692
|
+
def __call__(self, *args, **kwargs) -> DataSlice[T]:
|
693
|
+
return self._flow_fn(*args, **kwargs)
|
655
694
|
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
695
|
+
@property
|
696
|
+
def _flow_info(self) -> TransformFlowInfo:
|
697
|
+
if self._lazy_flow_info is not None:
|
698
|
+
return self._lazy_flow_info
|
699
|
+
return execution_context.run(self._flow_info_async())
|
700
|
+
|
701
|
+
async def _flow_info_async(self) -> TransformFlowInfo:
|
702
|
+
if self._lazy_flow_info is not None:
|
703
|
+
return self._lazy_flow_info
|
704
|
+
async with self._lazy_lock:
|
705
|
+
if self._lazy_flow_info is None:
|
706
|
+
self._lazy_flow_info = await self._build_flow_info_async()
|
707
|
+
return self._lazy_flow_info
|
708
|
+
|
709
|
+
async def _build_flow_info_async(self) -> TransformFlowInfo:
|
710
|
+
flow_builder_state = _FlowBuilderState(self._flow_name)
|
711
|
+
sig = inspect.signature(self._flow_fn)
|
712
|
+
if len(sig.parameters) != len(self._flow_arg_types):
|
660
713
|
raise ValueError(
|
661
714
|
f"Number of parameters in the flow function ({len(sig.parameters)}) "
|
662
|
-
"does not match the number of argument types ({len(
|
715
|
+
f"does not match the number of argument types ({len(self._flow_arg_types)})")
|
663
716
|
|
664
717
|
kwargs: dict[str, DataSlice] = {}
|
665
|
-
for (param_name, param), param_type in zip(sig.parameters.items(),
|
718
|
+
for (param_name, param), param_type in zip(sig.parameters.items(), self._flow_arg_types):
|
666
719
|
if param.kind not in (inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
667
720
|
inspect.Parameter.KEYWORD_ONLY):
|
668
721
|
raise ValueError(f"Parameter {param_name} is not a parameter can be passed by name")
|
@@ -670,20 +723,68 @@ class TransientFlow:
|
|
670
723
|
param_name, encode_enriched_type(param_type))
|
671
724
|
kwargs[param_name] = DataSlice(_DataSliceState(flow_builder_state, engine_ds))
|
672
725
|
|
673
|
-
output =
|
726
|
+
output = self._flow_fn(**kwargs)
|
674
727
|
flow_builder_state.engine_flow_builder.set_direct_output(
|
675
728
|
_data_slice_state(output).engine_data_slice)
|
676
|
-
|
677
|
-
|
729
|
+
engine_flow = await flow_builder_state.engine_flow_builder.build_transient_flow_async(execution_context.event_loop)
|
730
|
+
self._param_names = list(sig.parameters.keys())
|
731
|
+
|
732
|
+
engine_return_type = _data_slice_state(output).engine_data_slice.data_type().schema()
|
733
|
+
python_return_type = _get_data_slice_annotation_type(sig.return_annotation)
|
734
|
+
result_decoder = make_engine_value_decoder([], engine_return_type['type'], python_return_type)
|
735
|
+
|
736
|
+
return TransformFlowInfo(engine_flow, result_decoder)
|
678
737
|
|
679
738
|
def __str__(self):
|
680
|
-
return str(self.
|
739
|
+
return str(self._flow_info.engine_flow)
|
681
740
|
|
682
741
|
def __repr__(self):
|
683
|
-
return repr(self.
|
742
|
+
return repr(self._flow_info.engine_flow)
|
684
743
|
|
685
744
|
def internal_flow(self) -> _engine.TransientFlow:
|
686
745
|
"""
|
687
746
|
Get the internal flow.
|
688
747
|
"""
|
689
|
-
return self.
|
748
|
+
return self._flow_info.engine_flow
|
749
|
+
|
750
|
+
def eval(self, *args, **kwargs) -> T:
|
751
|
+
"""
|
752
|
+
Evaluate the transform flow.
|
753
|
+
"""
|
754
|
+
return execution_context.run(self.eval_async(*args, **kwargs))
|
755
|
+
|
756
|
+
async def eval_async(self, *args, **kwargs) -> T:
|
757
|
+
"""
|
758
|
+
Evaluate the transform flow.
|
759
|
+
"""
|
760
|
+
flow_info = await self._flow_info_async()
|
761
|
+
params = []
|
762
|
+
for i, arg in enumerate(self._param_names):
|
763
|
+
if i < len(args):
|
764
|
+
params.append(encode_engine_value(args[i]))
|
765
|
+
elif arg in kwargs:
|
766
|
+
params.append(encode_engine_value(kwargs[arg]))
|
767
|
+
else:
|
768
|
+
raise ValueError(f"Parameter {arg} is not provided")
|
769
|
+
engine_result = await flow_info.engine_flow.evaluate_async(params)
|
770
|
+
return flow_info.result_decoder(engine_result)
|
771
|
+
|
772
|
+
|
773
|
+
def transform_flow() -> Callable[[Callable[..., DataSlice[T]]], TransformFlow[T]]:
|
774
|
+
"""
|
775
|
+
A decorator to wrap the transform function.
|
776
|
+
"""
|
777
|
+
def _transform_flow_wrapper(fn: Callable[..., DataSlice[T]]):
|
778
|
+
sig = inspect.signature(fn)
|
779
|
+
arg_types = []
|
780
|
+
for (param_name, param) in sig.parameters.items():
|
781
|
+
if param.kind not in (inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
782
|
+
inspect.Parameter.KEYWORD_ONLY):
|
783
|
+
raise ValueError(f"Parameter {param_name} is not a parameter can be passed by name")
|
784
|
+
arg_types.append(_get_data_slice_annotation_type(param.annotation))
|
785
|
+
|
786
|
+
_transform_flow = TransformFlow(fn, arg_types)
|
787
|
+
functools.update_wrapper(_transform_flow, fn)
|
788
|
+
return _transform_flow
|
789
|
+
|
790
|
+
return _transform_flow_wrapper
|
cocoindex/op.py
CHANGED
@@ -100,8 +100,8 @@ def _register_op_factory(
|
|
100
100
|
return op_args.behavior_version
|
101
101
|
|
102
102
|
class _WrappedClass(executor_cls, _Fallback):
|
103
|
-
|
104
|
-
|
103
|
+
_args_decoders: list[Callable[[Any], Any]]
|
104
|
+
_kwargs_decoders: dict[str, Callable[[str, Any], Any]]
|
105
105
|
_acall: Callable
|
106
106
|
|
107
107
|
def __init__(self, spec):
|
@@ -109,17 +109,17 @@ def _register_op_factory(
|
|
109
109
|
self.spec = spec
|
110
110
|
self._acall = _to_async_call(super().__call__)
|
111
111
|
|
112
|
-
def analyze(self, *args, **kwargs):
|
112
|
+
def analyze(self, *args: _engine.OpArgSchema, **kwargs: _engine.OpArgSchema):
|
113
113
|
"""
|
114
114
|
Analyze the spec and arguments. In this phase, argument types should be validated.
|
115
115
|
It should return the expected result type for the current op.
|
116
116
|
"""
|
117
|
-
self.
|
118
|
-
self.
|
117
|
+
self._args_decoders = []
|
118
|
+
self._kwargs_decoders = {}
|
119
119
|
|
120
120
|
# Match arguments with parameters.
|
121
121
|
next_param_idx = 0
|
122
|
-
for arg in
|
122
|
+
for arg in args:
|
123
123
|
if next_param_idx >= len(expected_args):
|
124
124
|
raise ValueError(
|
125
125
|
f"Too many arguments passed in: {len(args)} > {len(expected_args)}")
|
@@ -128,7 +128,7 @@ def _register_op_factory(
|
|
128
128
|
inspect.Parameter.KEYWORD_ONLY, inspect.Parameter.VAR_KEYWORD):
|
129
129
|
raise ValueError(
|
130
130
|
f"Too many positional arguments passed in: {len(args)} > {next_param_idx}")
|
131
|
-
self.
|
131
|
+
self._args_decoders.append(
|
132
132
|
make_engine_value_decoder(
|
133
133
|
[arg_name], arg.value_type['type'], arg_param.annotation))
|
134
134
|
if arg_param.kind != inspect.Parameter.VAR_POSITIONAL:
|
@@ -146,7 +146,7 @@ def _register_op_factory(
|
|
146
146
|
if expected_arg is None:
|
147
147
|
raise ValueError(f"Unexpected keyword argument passed in: {kwarg_name}")
|
148
148
|
arg_param = expected_arg[1]
|
149
|
-
self.
|
149
|
+
self._kwargs_decoders[kwarg_name] = make_engine_value_decoder(
|
150
150
|
[kwarg_name], kwarg.value_type['type'], arg_param.annotation)
|
151
151
|
|
152
152
|
missing_args = [name for (name, arg) in expected_kwargs
|
@@ -174,8 +174,8 @@ def _register_op_factory(
|
|
174
174
|
await _to_async_call(setup_method)()
|
175
175
|
|
176
176
|
async def __call__(self, *args, **kwargs):
|
177
|
-
|
178
|
-
|
177
|
+
decoded_args = (decoder(arg) for decoder, arg in zip(self._args_decoders, args))
|
178
|
+
decoded_kwargs = {arg_name: self._kwargs_decoders[arg_name](arg)
|
179
179
|
for arg_name, arg in kwargs.items()}
|
180
180
|
|
181
181
|
if op_args.gpu:
|
@@ -185,9 +185,9 @@ def _register_op_factory(
|
|
185
185
|
# For now, we use a lock to ensure only one task is executed at a time.
|
186
186
|
# TODO: Implement multi-processing dispatching.
|
187
187
|
async with _gpu_dispatch_lock:
|
188
|
-
output = await self._acall(*
|
188
|
+
output = await self._acall(*decoded_args, **decoded_kwargs)
|
189
189
|
else:
|
190
|
-
output = await self._acall(*
|
190
|
+
output = await self._acall(*decoded_args, **decoded_kwargs)
|
191
191
|
return encode_engine_value(output)
|
192
192
|
|
193
193
|
_WrappedClass.__name__ = executor_cls.__name__
|
cocoindex/query.py
CHANGED
@@ -50,7 +50,7 @@ class SimpleSemanticsQueryHandler:
|
|
50
50
|
if engine_handler is None:
|
51
51
|
engine_handler = _engine.SimpleSemanticsQueryHandler(
|
52
52
|
flow.internal_flow(), target_name,
|
53
|
-
fl.
|
53
|
+
fl.TransformFlow(query_transform_flow, [str]).internal_flow(),
|
54
54
|
default_similarity_metric.value)
|
55
55
|
engine_handler.register_query_handler(name)
|
56
56
|
return engine_handler
|
cocoindex/setting.py
CHANGED
@@ -49,7 +49,7 @@ def _load_field(target: dict[str, Any], name: str, env_name: str, required: bool
|
|
49
49
|
class Settings:
|
50
50
|
"""Settings for the cocoindex library."""
|
51
51
|
database: DatabaseConnectionSpec
|
52
|
-
app_namespace: str
|
52
|
+
app_namespace: str = ""
|
53
53
|
|
54
54
|
@classmethod
|
55
55
|
def from_env(cls) -> Self:
|
cocoindex/utils.py
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
from .flow import Flow
|
2
|
+
from .setting import get_app_namespace
|
3
|
+
|
4
|
+
def get_target_storage_default_name(flow: Flow, target_name: str, delimiter: str = "__") -> str:
|
5
|
+
"""
|
6
|
+
Get the default name for a target.
|
7
|
+
It's used as the underlying storage name (e.g. a table, a collection, etc.) followed by most storage backends, if not explicitly specified.
|
8
|
+
"""
|
9
|
+
return get_app_namespace(trailing_delimiter=delimiter) + flow.name + delimiter + target_name
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: cocoindex
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.40
|
4
4
|
Requires-Dist: sentence-transformers>=3.3.1
|
5
5
|
Requires-Dist: click>=8.1.8
|
6
6
|
Requires-Dist: rich>=14.0.0
|
@@ -153,7 +153,7 @@ It defines an index flow like this:
|
|
153
153
|
| [Docs to Knowledge Graph](examples/docs_to_knowledge_graph) | Extract relationships from Markdown documents and build a knowledge graph |
|
154
154
|
| [Embeddings to Qdrant](examples/text_embedding_qdrant) | Index documents in a Qdrant collection for semantic search |
|
155
155
|
| [FastAPI Server with Docker](examples/fastapi_server_docker) | Run the semantic search server in a Dockerized FastAPI setup |
|
156
|
-
| [
|
156
|
+
| [Product Recommendation](examples/product_recommendation) | Build real-time product recommendations with LLM and graph database|
|
157
157
|
| [Image Search with Vision API](examples/image_search_example) | Generates detailed captions for images using a vision model, embeds them, enables live-updating semantic search via FastAPI and served on a React frontend|
|
158
158
|
|
159
159
|
More coming and stay tuned 👀!
|
@@ -1,25 +1,26 @@
|
|
1
|
-
cocoindex-0.1.
|
2
|
-
cocoindex-0.1.
|
3
|
-
cocoindex-0.1.
|
4
|
-
cocoindex/__init__.py,sha256=
|
5
|
-
cocoindex/_engine.cpython-313-darwin.so,sha256=
|
1
|
+
cocoindex-0.1.40.dist-info/METADATA,sha256=k_dUmyVymd4d0TmoRNrOnQAf8r9bOgLzqQU5MWTFZe8,9790
|
2
|
+
cocoindex-0.1.40.dist-info/WHEEL,sha256=zAURN41HE7eECKRZmuiOtDfTjqy9EVWhOpFIBiTRrcw,106
|
3
|
+
cocoindex-0.1.40.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
4
|
+
cocoindex/__init__.py,sha256=NIOdQAQ2nMgLgMdfGsWj4YnmC6sFWDUE1-33hkS63T4,799
|
5
|
+
cocoindex/_engine.cpython-313-darwin.so,sha256=kzXfp5opv7BWv4LWVYXDVl-WG2ycIyEfM1gzhoF4_F0,59304824
|
6
6
|
cocoindex/auth_registry.py,sha256=NsALZ3SKsDG9cPdrlTlalIqUvgbgFOaFGAbWJNedtJE,692
|
7
7
|
cocoindex/cli.py,sha256=Ac3ybnQW-HGVGJeUwIOHd1qhjs0KC5wCsemWuyouEfU,8999
|
8
|
-
cocoindex/convert.py,sha256=
|
9
|
-
cocoindex/flow.py,sha256=
|
8
|
+
cocoindex/convert.py,sha256=75HSBie7DokM0RJyUBqeveZRl5y_Fl8lzByoRF0yb2M,6915
|
9
|
+
cocoindex/flow.py,sha256=QRbbYwHCpOvjAah33gbuVLgu1ML_ea0HReHHOFDTHxQ,27888
|
10
10
|
cocoindex/functions.py,sha256=F79dNmGE127LaU67kF5Oqtf_tIzebFQH7MkyceMX4-s,1830
|
11
11
|
cocoindex/index.py,sha256=LssEOuZi6AqhwKtZM3QFeQpa9T-0ELi8G5DsrYKECvc,534
|
12
12
|
cocoindex/lib.py,sha256=OqTMuOHicdyX9PRA7fmTzznK8HZMrzxpUDbqxAEF--Q,2383
|
13
13
|
cocoindex/llm.py,sha256=_3rtahuKcqcEHPkFSwhXOSrekZyGxVApPoYtlU_chcA,348
|
14
|
-
cocoindex/op.py,sha256=
|
14
|
+
cocoindex/op.py,sha256=yyB3gYYj6uIeoGW9FXuj9Ludaz50QYDeqGgi3dKG1_I,10739
|
15
15
|
cocoindex/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
|
-
cocoindex/query.py,sha256=
|
16
|
+
cocoindex/query.py,sha256=As-6xIEyr27dN8txF9CsCA9KeL6lyKR7bU6PW4RMBl0,3205
|
17
17
|
cocoindex/runtime.py,sha256=jqRnWkkIlAhE04gi4y0Y5bzuq9FX4j0aVNU-nengLJk,980
|
18
|
-
cocoindex/setting.py,sha256=
|
18
|
+
cocoindex/setting.py,sha256=5sywzYWnUNv80lNDwFlkFkUOoW0wo4n4DEuhFZxryxA,3265
|
19
19
|
cocoindex/setup.py,sha256=ErNtX08NfFOFKehp5qGUvCx8Wiz9f3gmzvfBhAqrQyI,745
|
20
20
|
cocoindex/sources.py,sha256=7lpwYLsFCRfbURKf79Vu0JZZoXjAYY0DxNHzUb-VHBY,1327
|
21
21
|
cocoindex/storages.py,sha256=MFMsfyOCYMggTWeWrOi82miqOXQmiUuqq828x5htBr0,2207
|
22
22
|
cocoindex/tests/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
23
23
|
cocoindex/tests/test_convert.py,sha256=7jc--I3frrg7DB5MPr4JFzE7DSCznJuWyHdlDLQJ_fM,15516
|
24
24
|
cocoindex/typing.py,sha256=369ABRtnpbaVSQVIBc2ZDutXW8jUmncvNJd9CHEWT3Q,8962
|
25
|
-
cocoindex
|
25
|
+
cocoindex/utils.py,sha256=eClhMdjBjcXaDkah-rPUmE7Y5Ncd7S1goUe2qTETR08,456
|
26
|
+
cocoindex-0.1.40.dist-info/RECORD,,
|
File without changes
|
File without changes
|