inputlayer-client-dev 0.1.0.dev912__py3-none-any.whl → 0.1.0.dev913__py3-none-any.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.
- inputlayer/integrations/langchain/retriever.py +26 -3
- inputlayer/integrations/langchain/tool.py +38 -18
- inputlayer/integrations/langchain/vector_store.py +29 -9
- {inputlayer_client_dev-0.1.0.dev912.dist-info → inputlayer_client_dev-0.1.0.dev913.dist-info}/METADATA +1 -1
- {inputlayer_client_dev-0.1.0.dev912.dist-info → inputlayer_client_dev-0.1.0.dev913.dist-info}/RECORD +7 -7
- {inputlayer_client_dev-0.1.0.dev912.dist-info → inputlayer_client_dev-0.1.0.dev913.dist-info}/WHEEL +0 -0
- {inputlayer_client_dev-0.1.0.dev912.dist-info → inputlayer_client_dev-0.1.0.dev913.dist-info}/entry_points.txt +0 -0
|
@@ -21,6 +21,21 @@ from inputlayer.integrations.langchain.params import bind_params
|
|
|
21
21
|
|
|
22
22
|
logger = logging.getLogger(__name__)
|
|
23
23
|
|
|
24
|
+
|
|
25
|
+
def _extract_error_message(result: Any) -> str:
|
|
26
|
+
"""Extract the error text from an ``columns == ["error"]`` result.
|
|
27
|
+
|
|
28
|
+
Guards against missing rows and empty rows so the retriever never
|
|
29
|
+
crashes with IndexError when the engine sends a malformed error
|
|
30
|
+
envelope.
|
|
31
|
+
"""
|
|
32
|
+
rows = getattr(result, "rows", None) or []
|
|
33
|
+
if not rows or not rows[0]:
|
|
34
|
+
return "unknown error"
|
|
35
|
+
msg = rows[0][0]
|
|
36
|
+
return str(msg) if msg is not None else "unknown error"
|
|
37
|
+
|
|
38
|
+
|
|
24
39
|
class InputLayerRetriever(BaseRetriever):
|
|
25
40
|
"""Retrieve documents from an InputLayer KnowledgeGraph.
|
|
26
41
|
|
|
@@ -59,6 +74,11 @@ class InputLayerRetriever(BaseRetriever):
|
|
|
59
74
|
)
|
|
60
75
|
|
|
61
76
|
Both sync and async paths are supported natively.
|
|
77
|
+
|
|
78
|
+
When the engine rejects a query (parse error, unknown relation,
|
|
79
|
+
etc.) the retriever raises ``RuntimeError`` with the engine message
|
|
80
|
+
rather than returning empty results, so a misconfigured chain fails
|
|
81
|
+
loudly instead of silently producing zero documents.
|
|
62
82
|
"""
|
|
63
83
|
|
|
64
84
|
kg: Any # KnowledgeGraph - typed as Any for Pydantic compatibility
|
|
@@ -145,8 +165,9 @@ class InputLayerRetriever(BaseRetriever):
|
|
|
145
165
|
logger.debug("IQL retriever query: %s", compiled)
|
|
146
166
|
result = await self.kg.execute(compiled)
|
|
147
167
|
if result.columns == ["error"]:
|
|
148
|
-
|
|
149
|
-
|
|
168
|
+
raise RuntimeError(
|
|
169
|
+
f"InputLayer rejected query: {_extract_error_message(result)}"
|
|
170
|
+
)
|
|
150
171
|
return self._to_documents(result.columns, result.rows, hidden_columns=set())
|
|
151
172
|
|
|
152
173
|
def _resolve_params(self, user_query: str) -> dict[str, Any]:
|
|
@@ -244,7 +265,9 @@ class InputLayerRetriever(BaseRetriever):
|
|
|
244
265
|
|
|
245
266
|
resolved_content: list[str] = []
|
|
246
267
|
content_cols = (
|
|
247
|
-
self.page_content_columns
|
|
268
|
+
self.page_content_columns
|
|
269
|
+
if self.page_content_columns is not None
|
|
270
|
+
else ["content"]
|
|
248
271
|
)
|
|
249
272
|
explicit_content = self.page_content_columns is not None
|
|
250
273
|
for c in content_cols:
|
|
@@ -82,6 +82,10 @@ class InputLayerIQLTool(BaseTool):
|
|
|
82
82
|
placeholder; the agent's input is safely bound (escaped, quoted)
|
|
83
83
|
rather than spliced as raw text.
|
|
84
84
|
|
|
85
|
+
Engine errors are returned to the agent as ``"Error: <message>"`` so
|
|
86
|
+
the tool-calling LLM can observe the failure and adjust its next
|
|
87
|
+
action instead of the coroutine raising an exception mid-chain.
|
|
88
|
+
|
|
85
89
|
Prefer ``tools_from_relations`` for normal agent use.
|
|
86
90
|
"""
|
|
87
91
|
|
|
@@ -137,6 +141,8 @@ class InputLayerIQLTool(BaseTool):
|
|
|
137
141
|
|
|
138
142
|
logger.debug("IQL tool query: %s", compiled)
|
|
139
143
|
result = await self.kg.execute(compiled)
|
|
144
|
+
if result.columns == ["error"]:
|
|
145
|
+
return f"Error: {_extract_error_message(result)}"
|
|
140
146
|
return _format_result(result, self.max_rows)
|
|
141
147
|
|
|
142
148
|
|
|
@@ -171,7 +177,9 @@ def tools_from_relations(
|
|
|
171
177
|
agent = create_tool_calling_agent(llm, tools, prompt)
|
|
172
178
|
|
|
173
179
|
Returned tools emit JSON arrays of row dicts so tool-calling LLMs
|
|
174
|
-
can parse them directly.
|
|
180
|
+
can parse them directly. Engine errors are returned as a JSON
|
|
181
|
+
``{"error": "<message>"}`` object rather than raising, so the agent
|
|
182
|
+
can recover from a bad filter without aborting the chain.
|
|
175
183
|
"""
|
|
176
184
|
return [
|
|
177
185
|
_relation_to_tool(kg, r, max_rows=max_rows, name_prefix=name_prefix)
|
|
@@ -343,9 +351,7 @@ def _make_relation_runner(
|
|
|
343
351
|
logger.debug("Structured tool query: %s", q)
|
|
344
352
|
result = await kg.execute(q)
|
|
345
353
|
if result.columns == ["error"]:
|
|
346
|
-
return json.dumps(
|
|
347
|
-
{"error": result.rows[0][0] if result.rows else "unknown error"}
|
|
348
|
-
)
|
|
354
|
+
return json.dumps({"error": _extract_error_message(result)})
|
|
349
355
|
if not merged_columns:
|
|
350
356
|
merged_columns = result.columns
|
|
351
357
|
for row in result.rows:
|
|
@@ -359,8 +365,12 @@ def _make_relation_runner(
|
|
|
359
365
|
if len(merged_rows) >= max_rows + 1:
|
|
360
366
|
break
|
|
361
367
|
|
|
362
|
-
|
|
363
|
-
|
|
368
|
+
was_truncated = len(merged_rows) > max_rows
|
|
369
|
+
capped = merged_rows[:max_rows]
|
|
370
|
+
fake = _FakeResult(merged_columns, capped)
|
|
371
|
+
if was_truncated:
|
|
372
|
+
fake.row_count = max_rows + 1
|
|
373
|
+
return _format_result(fake, max_rows)
|
|
364
374
|
|
|
365
375
|
run.__name__ = f"search_{rel_name}"
|
|
366
376
|
run.parse_clauses = parse_clauses # type: ignore[attr-defined]
|
|
@@ -393,6 +403,24 @@ def _hashable(v: Any) -> Any:
|
|
|
393
403
|
# ── Result formatting ────────────────────────────────────────────────
|
|
394
404
|
|
|
395
405
|
|
|
406
|
+
def _extract_error_message(result: Any) -> str:
|
|
407
|
+
"""Extract a single error message from an ``error`` result set.
|
|
408
|
+
|
|
409
|
+
The engine signals an error with ``columns == ["error"]`` and a single
|
|
410
|
+
row whose first cell is the message. Guard against both the no-row and
|
|
411
|
+
empty-row shapes so a malformed response never masquerades as a
|
|
412
|
+
successful result or crashes the caller.
|
|
413
|
+
"""
|
|
414
|
+
rows = getattr(result, "rows", None) or []
|
|
415
|
+
if not rows:
|
|
416
|
+
return "unknown error"
|
|
417
|
+
first = rows[0]
|
|
418
|
+
if not first:
|
|
419
|
+
return "unknown error"
|
|
420
|
+
msg = first[0]
|
|
421
|
+
return str(msg) if msg is not None else "unknown error"
|
|
422
|
+
|
|
423
|
+
|
|
396
424
|
def _format_result(result: Any, max_rows: int) -> str:
|
|
397
425
|
"""Format a ResultSet as a JSON array of row dicts.
|
|
398
426
|
|
|
@@ -403,18 +431,10 @@ def _format_result(result: Any, max_rows: int) -> str:
|
|
|
403
431
|
return "[]"
|
|
404
432
|
|
|
405
433
|
rows = result.rows[:max_rows]
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
logger.warning(
|
|
411
|
-
"Row length (%d) does not match column count (%d); "
|
|
412
|
-
"truncating to shorter. Columns: %r",
|
|
413
|
-
len(row), len(columns), columns,
|
|
414
|
-
)
|
|
415
|
-
payload.append(
|
|
416
|
-
{col: _jsonify(val) for col, val in zip(columns, row, strict=False)}
|
|
417
|
-
)
|
|
434
|
+
payload = [
|
|
435
|
+
{col: _jsonify(val) for col, val in zip(result.columns, row, strict=True)}
|
|
436
|
+
for row in rows
|
|
437
|
+
]
|
|
418
438
|
|
|
419
439
|
total = getattr(result, "row_count", len(result.rows)) or len(result.rows)
|
|
420
440
|
if total > max_rows:
|
|
@@ -72,6 +72,13 @@ class InputLayerVectorStore(VectorStore):
|
|
|
72
72
|
on the provider itself, e.g.
|
|
73
73
|
``OpenAIEmbeddings(timeout=30, max_retries=2)``.
|
|
74
74
|
|
|
75
|
+
.. note::
|
|
76
|
+
|
|
77
|
+
``add_texts`` validates that ``texts``, ``metadatas``, and ``ids``
|
|
78
|
+
all have the same length and raises ``ValueError`` up front.
|
|
79
|
+
Previously a mismatch would surface as an opaque ``zip`` error
|
|
80
|
+
deep inside the embedding call.
|
|
81
|
+
|
|
75
82
|
Usage::
|
|
76
83
|
|
|
77
84
|
class Chunk(Relation):
|
|
@@ -190,6 +197,17 @@ class InputLayerVectorStore(VectorStore):
|
|
|
190
197
|
if not texts_list:
|
|
191
198
|
return []
|
|
192
199
|
|
|
200
|
+
if metadatas is not None and len(metadatas) != len(texts_list):
|
|
201
|
+
raise ValueError(
|
|
202
|
+
f"Length mismatch: {len(texts_list)} texts but "
|
|
203
|
+
f"{len(metadatas)} metadata dicts"
|
|
204
|
+
)
|
|
205
|
+
if ids is not None and len(ids) != len(texts_list):
|
|
206
|
+
raise ValueError(
|
|
207
|
+
f"Length mismatch: {len(texts_list)} texts but "
|
|
208
|
+
f"{len(ids)} ids"
|
|
209
|
+
)
|
|
210
|
+
|
|
193
211
|
vectors = await self._embeddings.aembed_documents(texts_list)
|
|
194
212
|
ids_out = ids or [str(uuid.uuid4()) for _ in texts_list]
|
|
195
213
|
metas = metadatas or [{} for _ in texts_list]
|
|
@@ -448,13 +466,14 @@ class InputLayerVectorStore(VectorStore):
|
|
|
448
466
|
),
|
|
449
467
|
)
|
|
450
468
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
469
|
+
vec_col_idx = next(
|
|
470
|
+
(
|
|
471
|
+
i
|
|
472
|
+
for i, c in enumerate(result.columns)
|
|
473
|
+
if c.lower() == self._vector_field.lower()
|
|
474
|
+
),
|
|
475
|
+
None,
|
|
476
|
+
)
|
|
458
477
|
if vec_col_idx is None:
|
|
459
478
|
raise ValueError(
|
|
460
479
|
f"Could not find vector column {self._vector_field!r} in "
|
|
@@ -467,7 +486,7 @@ class InputLayerVectorStore(VectorStore):
|
|
|
467
486
|
out: list[tuple[Document, float, list[float]]] = []
|
|
468
487
|
docs_and_scores = self._rows_to_documents(result.columns, result.rows)
|
|
469
488
|
for (doc, score), row in zip(docs_and_scores, result.rows, strict=True):
|
|
470
|
-
raw = row[vec_col_idx]
|
|
489
|
+
raw = row[vec_col_idx]
|
|
471
490
|
vec = list(raw) if raw is not None else []
|
|
472
491
|
out.append((doc, score, vec))
|
|
473
492
|
return out
|
|
@@ -584,7 +603,8 @@ class InputLayerVectorStore(VectorStore):
|
|
|
584
603
|
if k.lower() == self._vector_field.lower():
|
|
585
604
|
continue
|
|
586
605
|
metadata[canonical(k)] = v
|
|
587
|
-
|
|
606
|
+
raw_score = row_dict.get(score_col) if score_col else None
|
|
607
|
+
score = float(raw_score) if raw_score is not None else 0.0
|
|
588
608
|
out.append((Document(page_content=content, metadata=metadata), score))
|
|
589
609
|
return out
|
|
590
610
|
|
{inputlayer_client_dev-0.1.0.dev912.dist-info → inputlayer_client_dev-0.1.0.dev913.dist-info}/RECORD
RENAMED
|
@@ -24,9 +24,9 @@ inputlayer/types.py,sha256=EfL2oqjxvfdNzZCKoJyBaobge4Z4cT1AJYEIzJIsPQo,6546
|
|
|
24
24
|
inputlayer/integrations/__init__.py,sha256=4feiWcNvqCShcgjsYid1AcrPxJiLwREQ3OMgnIaahxg,276
|
|
25
25
|
inputlayer/integrations/langchain/__init__.py,sha256=JHirtEbpniYtjmXzux7py1miAuIAwhGQX3PBwMylY3Y,1252
|
|
26
26
|
inputlayer/integrations/langchain/params.py,sha256=9VBcTcr0A4oKkyVDvAwPYNL2xraUa_COAFU60DZKjFA,4437
|
|
27
|
-
inputlayer/integrations/langchain/retriever.py,sha256=
|
|
28
|
-
inputlayer/integrations/langchain/tool.py,sha256
|
|
29
|
-
inputlayer/integrations/langchain/vector_store.py,sha256=
|
|
27
|
+
inputlayer/integrations/langchain/retriever.py,sha256=Q0Llppmb7pmN_it2ePm1wRC19UyfcIr7h15Mds0G6G8,13699
|
|
28
|
+
inputlayer/integrations/langchain/tool.py,sha256=-8t6w7r14nEFbymZ_iqE6V25HOXyn4ajw6HV-Uy34aI,16478
|
|
29
|
+
inputlayer/integrations/langchain/vector_store.py,sha256=7Db1sRgkHJvgLiC_6avbca0NusieIkmWE_8IqjVkuMA,23384
|
|
30
30
|
inputlayer/integrations/langgraph/__init__.py,sha256=nbZx0_mJlsHixdqcfQMmUSN8dLYkePou2k2LqwfIfqU,2345
|
|
31
31
|
inputlayer/integrations/langgraph/_checkpoint_serde.py,sha256=DWu7zTx4jLfBROMjzEFbFrEWK12LweYQ0WcxY5yDhsk,3294
|
|
32
32
|
inputlayer/integrations/langgraph/_checkpointer_mixin.py,sha256=N15WjHFJ89q_H9GGjWHsW2xtKJ_NIwbb0tZapV5c5Ac,12210
|
|
@@ -47,7 +47,7 @@ inputlayer/migrations/operations.py,sha256=_qiguq0GFcXVZgIJa4vHu7eeKSa5BUh2maDce
|
|
|
47
47
|
inputlayer/migrations/recorder.py,sha256=2LZyhifIyultiNLLKmUnqmi9voKtDfmOYVDmjC5hNxs,1499
|
|
48
48
|
inputlayer/migrations/state.py,sha256=BH5_ly9I9bVPn1lvhqS_UWS7NvYg2iCLJrbG50-mwlQ,3695
|
|
49
49
|
inputlayer/migrations/writer.py,sha256=XbQN5t5H538AASStAQ9jkrveYZwf2aFEFiXZsifUWI0,6465
|
|
50
|
-
inputlayer_client_dev-0.1.0.
|
|
51
|
-
inputlayer_client_dev-0.1.0.
|
|
52
|
-
inputlayer_client_dev-0.1.0.
|
|
53
|
-
inputlayer_client_dev-0.1.0.
|
|
50
|
+
inputlayer_client_dev-0.1.0.dev913.dist-info/METADATA,sha256=w78ZmmnmDRXK9i9sIxeZ6LbYAZAW2qO4EDtxx1rqqlk,19726
|
|
51
|
+
inputlayer_client_dev-0.1.0.dev913.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
52
|
+
inputlayer_client_dev-0.1.0.dev913.dist-info/entry_points.txt,sha256=Y90cH3HHrT10BztOigvvD1WdNIt_RYR6k6IsVbs_A8U,54
|
|
53
|
+
inputlayer_client_dev-0.1.0.dev913.dist-info/RECORD,,
|
{inputlayer_client_dev-0.1.0.dev912.dist-info → inputlayer_client_dev-0.1.0.dev913.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|