cocoindex 0.1.36__cp313-cp313-macosx_11_0_arm64.whl → 0.1.37__cp313-cp313-macosx_11_0_arm64.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.
Binary file
cocoindex/cli.py CHANGED
@@ -212,11 +212,12 @@ def server(address: str | None, live_update: bool, quiet: bool, cors_origin: str
212
212
 
213
213
  lib.start_server(server_settings)
214
214
 
215
+ if COCOINDEX_HOST in cors_origins:
216
+ click.echo(f"Open CocoInsight at: {COCOINDEX_HOST}/cocoinsight")
217
+
215
218
  if live_update:
216
219
  options = flow.FlowLiveUpdaterOptions(live_mode=True, print_stats=not quiet)
217
220
  flow.update_all_flows(options)
218
- if COCOINDEX_HOST in cors_origins:
219
- click.echo(f"Open CocoInsight at: {COCOINDEX_HOST}/cocoinsight")
220
221
  input("Press Enter to stop...")
221
222
 
222
223
 
cocoindex/convert.py CHANGED
@@ -8,13 +8,15 @@ import uuid
8
8
 
9
9
  from enum import Enum
10
10
  from typing import Any, Callable, get_origin
11
- from .typing import analyze_type_info, encode_enriched_type, TABLE_TYPES, KEY_FIELD_NAME
11
+ from .typing import analyze_type_info, encode_enriched_type, is_namedtuple_type, TABLE_TYPES, KEY_FIELD_NAME
12
12
 
13
13
 
14
14
  def encode_engine_value(value: Any) -> Any:
15
15
  """Encode a Python value to an engine value."""
16
16
  if dataclasses.is_dataclass(value):
17
17
  return [encode_engine_value(getattr(value, f.name)) for f in dataclasses.fields(value)]
18
+ if is_namedtuple_type(type(value)):
19
+ return [encode_engine_value(getattr(value, name)) for name in value._fields]
18
20
  if isinstance(value, (list, tuple)):
19
21
  return [encode_engine_value(v) for v in value]
20
22
  if isinstance(value, dict):
@@ -55,16 +57,16 @@ def make_engine_value_decoder(
55
57
  f"Type mismatch for `{''.join(field_path)}`: "
56
58
  f"passed in {src_type_kind}, declared {dst_annotation} ({dst_type_info.kind})")
57
59
 
58
- if dst_type_info.dataclass_type is not None:
60
+ if dst_type_info.struct_type is not None:
59
61
  return _make_engine_struct_value_decoder(
60
- field_path, src_type['fields'], dst_type_info.dataclass_type)
62
+ field_path, src_type['fields'], dst_type_info.struct_type)
61
63
 
62
64
  if src_type_kind in TABLE_TYPES:
63
65
  field_path.append('[*]')
64
66
  elem_type_info = analyze_type_info(dst_type_info.elem_type)
65
- if elem_type_info.dataclass_type is None:
67
+ if elem_type_info.struct_type is None:
66
68
  raise ValueError(f"Type mismatch for `{''.join(field_path)}`: "
67
- f"declared `{dst_type_info.kind}`, a dataclass type expected")
69
+ f"declared `{dst_type_info.kind}`, a dataclass or NamedTuple type expected")
68
70
  engine_fields_schema = src_type['row']['fields']
69
71
  if elem_type_info.key_type is not None:
70
72
  key_field_schema = engine_fields_schema[0]
@@ -73,14 +75,14 @@ def make_engine_value_decoder(
73
75
  field_path, key_field_schema['type'], elem_type_info.key_type)
74
76
  field_path.pop()
75
77
  value_decoder = _make_engine_struct_value_decoder(
76
- field_path, engine_fields_schema[1:], elem_type_info.dataclass_type)
78
+ field_path, engine_fields_schema[1:], elem_type_info.struct_type)
77
79
  def decode(value):
78
80
  if value is None:
79
81
  return None
80
82
  return {key_decoder(v[0]): value_decoder(v[1:]) for v in value}
81
83
  else:
82
84
  elem_decoder = _make_engine_struct_value_decoder(
83
- field_path, engine_fields_schema, elem_type_info.dataclass_type)
85
+ field_path, engine_fields_schema, elem_type_info.struct_type)
84
86
  def decode(value):
85
87
  if value is None:
86
88
  return None
@@ -96,11 +98,31 @@ def make_engine_value_decoder(
96
98
  def _make_engine_struct_value_decoder(
97
99
  field_path: list[str],
98
100
  src_fields: list[dict[str, Any]],
99
- dst_dataclass_type: type,
101
+ dst_struct_type: type,
100
102
  ) -> Callable[[list], Any]:
101
103
  """Make a decoder from an engine field values to a Python value."""
102
104
 
103
105
  src_name_to_idx = {f['name']: i for i, f in enumerate(src_fields)}
106
+
107
+ is_dataclass = dataclasses.is_dataclass(dst_struct_type)
108
+ is_namedtuple = is_namedtuple_type(dst_struct_type)
109
+
110
+ if is_dataclass:
111
+ parameters = inspect.signature(dst_struct_type).parameters
112
+ elif is_namedtuple:
113
+ defaults = getattr(dst_struct_type, '_field_defaults', {})
114
+ parameters = {
115
+ name: inspect.Parameter(
116
+ name=name,
117
+ kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
118
+ default=defaults.get(name, inspect.Parameter.empty),
119
+ annotation=dst_struct_type.__annotations__.get(name, inspect.Parameter.empty)
120
+ )
121
+ for name in dst_struct_type._fields
122
+ }
123
+ else:
124
+ raise ValueError(f"Unsupported struct type: {dst_struct_type}")
125
+
104
126
  def make_closure_for_value(name: str, param: inspect.Parameter) -> Callable[[list], Any]:
105
127
  src_idx = src_name_to_idx.get(name)
106
128
  if src_idx is not None:
@@ -108,7 +130,7 @@ def _make_engine_struct_value_decoder(
108
130
  field_decoder = make_engine_value_decoder(
109
131
  field_path, src_fields[src_idx]['type'], param.annotation)
110
132
  field_path.pop()
111
- return lambda values: field_decoder(values[src_idx])
133
+ return lambda values: field_decoder(values[src_idx]) if len(values) > src_idx else param.default
112
134
 
113
135
  default_value = param.default
114
136
  if default_value is inspect.Parameter.empty:
@@ -119,9 +141,9 @@ def _make_engine_struct_value_decoder(
119
141
 
120
142
  field_value_decoder = [
121
143
  make_closure_for_value(name, param)
122
- for (name, param) in inspect.signature(dst_dataclass_type).parameters.items()]
144
+ for (name, param) in parameters.items()]
123
145
 
124
- return lambda values: dst_dataclass_type(
146
+ return lambda values: dst_struct_type(
125
147
  *(decoder(values) for decoder in field_value_decoder))
126
148
 
127
149
  def dump_engine_object(v: Any) -> Any:
cocoindex/sources.py CHANGED
@@ -28,3 +28,16 @@ class GoogleDrive(op.SourceSpec):
28
28
  root_folder_ids: list[str]
29
29
  binary: bool = False
30
30
  recent_changes_poll_interval: datetime.timedelta | None = None
31
+
32
+
33
+ class AmazonS3(op.SourceSpec):
34
+ """Import data from an Amazon S3 bucket. Supports optional prefix and file filtering by glob patterns."""
35
+
36
+ _op_category = op.OpCategory.SOURCE
37
+
38
+ bucket_name: str
39
+ prefix: str | None = None
40
+ binary: bool = False
41
+ included_patterns: list[str] | None = None
42
+ excluded_patterns: list[str] | None = None
43
+ sqs_queue_url: str | None = None
@@ -1,11 +1,12 @@
1
1
  import uuid
2
2
  import datetime
3
3
  from dataclasses import dataclass, make_dataclass
4
+ from typing import NamedTuple, Literal
4
5
  import pytest
5
6
  import cocoindex
6
7
  from cocoindex.typing import encode_enriched_type
7
8
  from cocoindex.convert import encode_engine_value, make_engine_value_decoder
8
- from typing import Literal
9
+
9
10
  @dataclass
10
11
  class Order:
11
12
  order_id: str
@@ -33,6 +34,17 @@ class NestedStruct:
33
34
  orders: list[Order]
34
35
  count: int = 0
35
36
 
37
+ class OrderNamedTuple(NamedTuple):
38
+ order_id: str
39
+ name: str
40
+ price: float
41
+ extra_field: str = "default_extra"
42
+
43
+ class CustomerNamedTuple(NamedTuple):
44
+ name: str
45
+ order: OrderNamedTuple
46
+ tags: list[Tag] | None = None
47
+
36
48
  def build_engine_value_decoder(engine_type_in_py, python_type=None):
37
49
  """
38
50
  Helper to build a converter for the given engine-side type (as represented in Python).
@@ -62,10 +74,16 @@ def test_encode_engine_value_date_time_types():
62
74
  def test_encode_engine_value_struct():
63
75
  order = Order(order_id="O123", name="mixed nuts", price=25.0)
64
76
  assert encode_engine_value(order) == ["O123", "mixed nuts", 25.0, "default_extra"]
77
+
78
+ order_nt = OrderNamedTuple(order_id="O123", name="mixed nuts", price=25.0)
79
+ assert encode_engine_value(order_nt) == ["O123", "mixed nuts", 25.0, "default_extra"]
65
80
 
66
81
  def test_encode_engine_value_list_of_structs():
67
82
  orders = [Order("O1", "item1", 10.0), Order("O2", "item2", 20.0)]
68
83
  assert encode_engine_value(orders) == [["O1", "item1", 10.0, "default_extra"], ["O2", "item2", 20.0, "default_extra"]]
84
+
85
+ orders_nt = [OrderNamedTuple("O1", "item1", 10.0), OrderNamedTuple("O2", "item2", 20.0)]
86
+ assert encode_engine_value(orders_nt) == [["O1", "item1", 10.0, "default_extra"], ["O2", "item2", 20.0, "default_extra"]]
69
87
 
70
88
  def test_encode_engine_value_struct_with_list():
71
89
  basket = Basket(items=["apple", "banana"])
@@ -74,6 +92,9 @@ def test_encode_engine_value_struct_with_list():
74
92
  def test_encode_engine_value_nested_struct():
75
93
  customer = Customer(name="Alice", order=Order("O1", "item1", 10.0))
76
94
  assert encode_engine_value(customer) == ["Alice", ["O1", "item1", 10.0, "default_extra"], None]
95
+
96
+ customer_nt = CustomerNamedTuple(name="Alice", order=OrderNamedTuple("O1", "item1", 10.0))
97
+ assert encode_engine_value(customer_nt) == ["Alice", ["O1", "item1", 10.0, "default_extra"], None]
77
98
 
78
99
  def test_encode_engine_value_empty_list():
79
100
  assert encode_engine_value([]) == []
@@ -103,20 +124,34 @@ def test_make_engine_value_decoder_basic_types():
103
124
  @pytest.mark.parametrize(
104
125
  "data_type, engine_val, expected",
105
126
  [
106
- # All fields match
127
+ # All fields match (dataclass)
107
128
  (Order, ["O123", "mixed nuts", 25.0, "default_extra"], Order("O123", "mixed nuts", 25.0, "default_extra")),
129
+ # All fields match (NamedTuple)
130
+ (OrderNamedTuple, ["O123", "mixed nuts", 25.0, "default_extra"], OrderNamedTuple("O123", "mixed nuts", 25.0, "default_extra")),
108
131
  # Extra field in engine value (should ignore extra)
109
132
  (Order, ["O123", "mixed nuts", 25.0, "default_extra", "unexpected"], Order("O123", "mixed nuts", 25.0, "default_extra")),
133
+ (OrderNamedTuple, ["O123", "mixed nuts", 25.0, "default_extra", "unexpected"], OrderNamedTuple("O123", "mixed nuts", 25.0, "default_extra")),
110
134
  # Fewer fields in engine value (should fill with default)
111
135
  (Order, ["O123", "mixed nuts", 0.0, "default_extra"], Order("O123", "mixed nuts", 0.0, "default_extra")),
136
+ (OrderNamedTuple, ["O123", "mixed nuts", 0.0, "default_extra"], OrderNamedTuple("O123", "mixed nuts", 0.0, "default_extra")),
112
137
  # More fields in engine value (should ignore extra)
113
138
  (Order, ["O123", "mixed nuts", 25.0, "unexpected"], Order("O123", "mixed nuts", 25.0, "unexpected")),
139
+ (OrderNamedTuple, ["O123", "mixed nuts", 25.0, "unexpected"], OrderNamedTuple("O123", "mixed nuts", 25.0, "unexpected")),
114
140
  # Truly extra field (should ignore the fifth field)
115
141
  (Order, ["O123", "mixed nuts", 25.0, "default_extra", "ignored"], Order("O123", "mixed nuts", 25.0, "default_extra")),
142
+ (OrderNamedTuple, ["O123", "mixed nuts", 25.0, "default_extra", "ignored"], OrderNamedTuple("O123", "mixed nuts", 25.0, "default_extra")),
116
143
  # Missing optional field in engine value (tags=None)
117
144
  (Customer, ["Alice", ["O1", "item1", 10.0, "default_extra"], None], Customer("Alice", Order("O1", "item1", 10.0, "default_extra"), None)),
145
+ (CustomerNamedTuple, ["Alice", ["O1", "item1", 10.0, "default_extra"], None], CustomerNamedTuple("Alice", OrderNamedTuple("O1", "item1", 10.0, "default_extra"), None)),
118
146
  # Extra field in engine value for Customer (should ignore)
119
147
  (Customer, ["Alice", ["O1", "item1", 10.0, "default_extra"], [["vip"]], "extra"], Customer("Alice", Order("O1", "item1", 10.0, "default_extra"), [Tag("vip")])),
148
+ (CustomerNamedTuple, ["Alice", ["O1", "item1", 10.0, "default_extra"], [["vip"]], "extra"], CustomerNamedTuple("Alice", OrderNamedTuple("O1", "item1", 10.0, "default_extra"), [Tag("vip")])),
149
+ # Missing optional field with default
150
+ (Order, ["O123", "mixed nuts", 25.0], Order("O123", "mixed nuts", 25.0, "default_extra")),
151
+ (OrderNamedTuple, ["O123", "mixed nuts", 25.0], OrderNamedTuple("O123", "mixed nuts", 25.0, "default_extra")),
152
+ # Partial optional fields
153
+ (Customer, ["Alice", ["O1", "item1", 10.0]], Customer("Alice", Order("O1", "item1", 10.0, "default_extra"), None)),
154
+ (CustomerNamedTuple, ["Alice", ["O1", "item1", 10.0]], CustomerNamedTuple("Alice", OrderNamedTuple("O1", "item1", 10.0, "default_extra"), None)),
120
155
  ]
121
156
  )
122
157
  def test_struct_decoder_cases(data_type, engine_val, expected):
@@ -124,17 +159,27 @@ def test_struct_decoder_cases(data_type, engine_val, expected):
124
159
  assert decoder(engine_val) == expected
125
160
 
126
161
  def test_make_engine_value_decoder_collections():
127
- # List of structs
162
+ # List of structs (dataclass)
128
163
  decoder = build_engine_value_decoder(list[Order])
129
164
  engine_val = [
130
165
  ["O1", "item1", 10.0, "default_extra"],
131
166
  ["O2", "item2", 20.0, "default_extra"]
132
167
  ]
133
168
  assert decoder(engine_val) == [Order("O1", "item1", 10.0, "default_extra"), Order("O2", "item2", 20.0, "default_extra")]
169
+
170
+ # List of structs (NamedTuple)
171
+ decoder = build_engine_value_decoder(list[OrderNamedTuple])
172
+ assert decoder(engine_val) == [OrderNamedTuple("O1", "item1", 10.0, "default_extra"), OrderNamedTuple("O2", "item2", 20.0, "default_extra")]
173
+
134
174
  # Struct with list field
135
175
  decoder = build_engine_value_decoder(Customer)
136
176
  engine_val = ["Alice", ["O1", "item1", 10.0, "default_extra"], [["vip"], ["premium"]]]
137
177
  assert decoder(engine_val) == Customer("Alice", Order("O1", "item1", 10.0, "default_extra"), [Tag("vip"), Tag("premium")])
178
+
179
+ # NamedTuple with list field
180
+ decoder = build_engine_value_decoder(CustomerNamedTuple)
181
+ assert decoder(engine_val) == CustomerNamedTuple("Alice", OrderNamedTuple("O1", "item1", 10.0, "default_extra"), [Tag("vip"), Tag("premium")])
182
+
138
183
  # Struct with struct field
139
184
  decoder = build_engine_value_decoder(NestedStruct)
140
185
  engine_val = [
@@ -239,6 +284,13 @@ def test_roundtrip_ltable():
239
284
  assert encoded == [["O1", "item1", 10.0, "default_extra"], ["O2", "item2", 20.0, "default_extra"]]
240
285
  decoded = build_engine_value_decoder(t)(encoded)
241
286
  assert decoded == value
287
+
288
+ t_nt = list[OrderNamedTuple]
289
+ value_nt = [OrderNamedTuple("O1", "item1", 10.0), OrderNamedTuple("O2", "item2", 20.0)]
290
+ encoded = encode_engine_value(value_nt)
291
+ assert encoded == [["O1", "item1", 10.0, "default_extra"], ["O2", "item2", 20.0, "default_extra"]]
292
+ decoded = build_engine_value_decoder(t_nt)(encoded)
293
+ assert decoded == value_nt
242
294
 
243
295
  def test_roundtrip_ktable_str_key():
244
296
  t = dict[str, Order]
@@ -247,6 +299,13 @@ def test_roundtrip_ktable_str_key():
247
299
  assert encoded == [["K1", "O1", "item1", 10.0, "default_extra"], ["K2", "O2", "item2", 20.0, "default_extra"]]
248
300
  decoded = build_engine_value_decoder(t)(encoded)
249
301
  assert decoded == value
302
+
303
+ t_nt = dict[str, OrderNamedTuple]
304
+ value_nt = {"K1": OrderNamedTuple("O1", "item1", 10.0), "K2": OrderNamedTuple("O2", "item2", 20.0)}
305
+ encoded = encode_engine_value(value_nt)
306
+ assert encoded == [["K1", "O1", "item1", 10.0, "default_extra"], ["K2", "O2", "item2", 20.0, "default_extra"]]
307
+ decoded = build_engine_value_decoder(t_nt)(encoded)
308
+ assert decoded == value_nt
250
309
 
251
310
  def test_roundtrip_ktable_struct_key():
252
311
  @dataclass(frozen=True)
@@ -261,6 +320,14 @@ def test_roundtrip_ktable_struct_key():
261
320
  [["B", 4], "O2", "item2", 20.0, "default_extra"]]
262
321
  decoded = build_engine_value_decoder(t)(encoded)
263
322
  assert decoded == value
323
+
324
+ t_nt = dict[OrderKey, OrderNamedTuple]
325
+ value_nt = {OrderKey("A", 3): OrderNamedTuple("O1", "item1", 10.0), OrderKey("B", 4): OrderNamedTuple("O2", "item2", 20.0)}
326
+ encoded = encode_engine_value(value_nt)
327
+ assert encoded == [[["A", 3], "O1", "item1", 10.0, "default_extra"],
328
+ [["B", 4], "O2", "item2", 20.0, "default_extra"]]
329
+ decoded = build_engine_value_decoder(t_nt)(encoded)
330
+ assert decoded == value_nt
264
331
 
265
332
  IntVectorType = cocoindex.Vector[int, Literal[5]]
266
333
  def test_vector_as_vector() -> None:
cocoindex/typing.py CHANGED
@@ -56,8 +56,11 @@ KEY_FIELD_NAME = '_key'
56
56
 
57
57
  ElementType = type | tuple[type, type]
58
58
 
59
+ def is_namedtuple_type(t) -> bool:
60
+ return isinstance(t, type) and issubclass(t, tuple) and hasattr(t, "_fields")
61
+
59
62
  def _is_struct_type(t) -> bool:
60
- return isinstance(t, type) and dataclasses.is_dataclass(t)
63
+ return isinstance(t, type) and (dataclasses.is_dataclass(t) or is_namedtuple_type(t))
61
64
 
62
65
  @dataclasses.dataclass
63
66
  class AnalyzedTypeInfo:
@@ -69,7 +72,7 @@ class AnalyzedTypeInfo:
69
72
  elem_type: ElementType | None # For Vector and Table
70
73
 
71
74
  key_type: type | None # For element of KTable
72
- dataclass_type: type | None # For Struct
75
+ struct_type: type | None # For Struct, a dataclass or namedtuple
73
76
 
74
77
  attrs: dict[str, Any] | None
75
78
  nullable: bool = False
@@ -117,15 +120,16 @@ def analyze_type_info(t) -> AnalyzedTypeInfo:
117
120
  elif isinstance(attr, TypeKind):
118
121
  kind = attr.kind
119
122
 
120
- dataclass_type = None
123
+ struct_type = None
121
124
  elem_type = None
122
125
  key_type = None
123
126
  if _is_struct_type(t):
127
+ struct_type = t
128
+
124
129
  if kind is None:
125
130
  kind = 'Struct'
126
131
  elif kind != 'Struct':
127
132
  raise ValueError(f"Unexpected type kind for struct: {kind}")
128
- dataclass_type = t
129
133
  elif base_type is collections.abc.Sequence or base_type is list:
130
134
  args = typing.get_args(t)
131
135
  elem_type = args[0]
@@ -167,36 +171,50 @@ def analyze_type_info(t) -> AnalyzedTypeInfo:
167
171
  else:
168
172
  raise ValueError(f"type unsupported yet: {t}")
169
173
 
170
- return AnalyzedTypeInfo(kind=kind, vector_info=vector_info,
171
- elem_type=elem_type, key_type=key_type, dataclass_type=dataclass_type,
172
- attrs=attrs, nullable=nullable)
173
-
174
- def _encode_fields_schema(dataclass_type: type, key_type: type | None = None) -> list[dict[str, Any]]:
174
+ return AnalyzedTypeInfo(
175
+ kind=kind,
176
+ vector_info=vector_info,
177
+ elem_type=elem_type,
178
+ key_type=key_type,
179
+ struct_type=struct_type,
180
+ attrs=attrs,
181
+ nullable=nullable,
182
+ )
183
+
184
+ def _encode_fields_schema(struct_type: type, key_type: type | None = None) -> list[dict[str, Any]]:
175
185
  result = []
176
186
  def add_field(name: str, t) -> None:
177
187
  try:
178
188
  type_info = encode_enriched_type_info(analyze_type_info(t))
179
189
  except ValueError as e:
180
- e.add_note(f"Failed to encode annotation for field - "
181
- f"{dataclass_type.__name__}.{name}: {t}")
190
+ e.add_note(
191
+ f"Failed to encode annotation for field - "
192
+ f"{struct_type.__name__}.{name}: {t}"
193
+ )
182
194
  raise
183
195
  type_info['name'] = name
184
196
  result.append(type_info)
185
197
 
186
198
  if key_type is not None:
187
199
  add_field(KEY_FIELD_NAME, key_type)
188
- for field in dataclasses.fields(dataclass_type):
189
- add_field(field.name, field.type)
200
+
201
+ if dataclasses.is_dataclass(struct_type):
202
+ for field in dataclasses.fields(struct_type):
203
+ add_field(field.name, field.type)
204
+ elif is_namedtuple_type(struct_type):
205
+ for name, field_type in struct_type.__annotations__.items():
206
+ add_field(name, field_type)
207
+
190
208
  return result
191
209
 
192
210
  def _encode_type(type_info: AnalyzedTypeInfo) -> dict[str, Any]:
193
211
  encoded_type: dict[str, Any] = { 'kind': type_info.kind }
194
212
 
195
213
  if type_info.kind == 'Struct':
196
- if type_info.dataclass_type is None:
197
- raise ValueError("Struct type must have a dataclass type")
198
- encoded_type['fields'] = _encode_fields_schema(type_info.dataclass_type, type_info.key_type)
199
- if doc := inspect.getdoc(type_info.dataclass_type):
214
+ if type_info.struct_type is None:
215
+ raise ValueError("Struct type must have a dataclass or namedtuple type")
216
+ encoded_type['fields'] = _encode_fields_schema(type_info.struct_type, type_info.key_type)
217
+ if doc := inspect.getdoc(type_info.struct_type):
200
218
  encoded_type['description'] = doc
201
219
 
202
220
  elif type_info.kind == 'Vector':
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cocoindex
3
- Version: 0.1.36
3
+ Version: 0.1.37
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
  | [Embeddings to Qdrant](examples/text_embedding_qdrant) | Index documents in a Qdrant collection for semantic search |
154
154
  | [FastAPI Server with Docker](examples/fastapi_server_docker) | Run the semantic search server in a Dockerized FastAPI setup |
155
155
  | [Product_Taxonomy_Knowledge_Graph](examples/product_taxonomy_knowledge_graph) | Build knowledge graph for product recommendations |
156
- | [Image Search with Vision API](examples/image_search_example) | Generates detailed captions for images using a vision model, embeds them, enables semantic search via FastAPI and served on a React frontend.|
156
+ | [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|
157
157
 
158
158
  More coming and stay tuned 👀!
159
159
 
@@ -1,11 +1,11 @@
1
- cocoindex-0.1.36.dist-info/METADATA,sha256=YH6UMfZ-m_ako8_9VXinagNN6kD6iVEh94Pv9Ua-C3U,9686
2
- cocoindex-0.1.36.dist-info/WHEEL,sha256=E1n5fBV87GjwiE9CayevgONfw5z1JsKZqIr2VxzumWg,104
3
- cocoindex-0.1.36.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
1
+ cocoindex-0.1.37.dist-info/METADATA,sha256=q_5xT2W7gjD_IiCl_pBr__gMfn3ldcoFppGNTmROkMY,9699
2
+ cocoindex-0.1.37.dist-info/WHEEL,sha256=UPfJ7S-gMCqCJ6cj5sliE010L87pWgiShNoxmys5TN4,104
3
+ cocoindex-0.1.37.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
4
4
  cocoindex/__init__.py,sha256=LpB0VjGvkD1beio8R9RCT6PI3eU0keV-3sBL45fHTQE,690
5
- cocoindex/_engine.cpython-313-darwin.so,sha256=mfESOHOd44ezhm_k4A8KGupMsNv6t8b8ws9KlCk6ckk,49734736
5
+ cocoindex/_engine.cpython-313-darwin.so,sha256=Bg9LtwGoESd3WjO_q8JAENVNoeHVZLtjc_M62ITZFmo,56739728
6
6
  cocoindex/auth_registry.py,sha256=NsALZ3SKsDG9cPdrlTlalIqUvgbgFOaFGAbWJNedtJE,692
7
- cocoindex/cli.py,sha256=QdZjgnABuDQfy6JiAxeAJiQMI5FNT9FQGLiYAUtLMw8,8923
8
- cocoindex/convert.py,sha256=mBUTa_Ag39_ut-yE_jc1wqS3zLjtOm6QKet-bqJ-RWc,5947
7
+ cocoindex/cli.py,sha256=1PJTdwX-X9r6Obw_W7VZDUgd5E7Xw6tLyvSDT8dFnY8,8924
8
+ cocoindex/convert.py,sha256=tRY-QBeeFMFwCYiRk7a0_tuDqopw8iqBpg_Aswcq9JQ,6864
9
9
  cocoindex/flow.py,sha256=MZZ0Uf0ObAzR1yIjUecRgA-U0t__95eoLBK_DxwwLnk,23375
10
10
  cocoindex/functions.py,sha256=F79dNmGE127LaU67kF5Oqtf_tIzebFQH7MkyceMX4-s,1830
11
11
  cocoindex/index.py,sha256=LssEOuZi6AqhwKtZM3QFeQpa9T-0ELi8G5DsrYKECvc,534
@@ -17,9 +17,9 @@ cocoindex/query.py,sha256=8_3Lb_EVjZtl2ZyJNZGX16LoKXEd-PL8OjY-zs9GQeA,3205
17
17
  cocoindex/runtime.py,sha256=jqRnWkkIlAhE04gi4y0Y5bzuq9FX4j0aVNU-nengLJk,980
18
18
  cocoindex/setting.py,sha256=pms1blwlXIOqZIpye-rfiwzqYUCAC8oEL7mQM5A160g,2356
19
19
  cocoindex/setup.py,sha256=AQLbtBLuJX066IANS7BGp20246mAGQ_4Z0W6MVJcQzY,481
20
- cocoindex/sources.py,sha256=wZFU8lwSXjyofJR-syySH9fTyPnBlAPJ6-1hQNX8fGA,936
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
- cocoindex/tests/test_convert.py,sha256=WPRKp0jv_uSEM81RGWEAmsax-J-FtXt90mZ0yEnvGLs,11236
24
- cocoindex/typing.py,sha256=BI2vPw4Iu4S3aznNJQrfM2LZU_weGYASTXF1W3ZWh_Y,8568
25
- cocoindex-0.1.36.dist-info/RECORD,,
23
+ cocoindex/tests/test_convert.py,sha256=7jc--I3frrg7DB5MPr4JFzE7DSCznJuWyHdlDLQJ_fM,15516
24
+ cocoindex/typing.py,sha256=369ABRtnpbaVSQVIBc2ZDutXW8jUmncvNJd9CHEWT3Q,8962
25
+ cocoindex-0.1.37.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: maturin (1.8.4)
2
+ Generator: maturin (1.8.6)
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp313-cp313-macosx_11_0_arm64