@pentatonic-ai/ai-agent-sdk 0.8.2 → 0.8.3
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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pentatonic-ai/ai-agent-sdk",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"description": "TES SDK — LLM observability and lifecycle tracking via Pentatonic Thing Event System. Track token usage, tool calls, and conversations. Manage things through event-sourced lifecycle stages with AI enrichment and vector search.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -86,7 +86,12 @@ log = logging.getLogger("l6-document-store")
|
|
|
86
86
|
# Embedding
|
|
87
87
|
# ---------------------------------------------------------------------------
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
# HTTP client for Ollama entity extraction (extract_entities below). Named
|
|
90
|
+
# `_ollama_http`, not `_embed_client`, because the embedding HTTP client now
|
|
91
|
+
# lives behind the EmbedClient factory above — sharing the `_embed_client`
|
|
92
|
+
# identifier caused a TypeError in v0.8.0–0.8.2 where the legacy module-level
|
|
93
|
+
# binding shadowed the factory function.
|
|
94
|
+
_ollama_http = httpx.Client(timeout=60)
|
|
90
95
|
|
|
91
96
|
def embed_text(text: str) -> List[float]:
|
|
92
97
|
"""Single-text embed via _embed_post (OpenAI-compat first, lambda-gateway fallback)."""
|
|
@@ -141,7 +146,7 @@ def rerank(query: str, results: List[Dict], top_k: int = 10) -> List[Dict]:
|
|
|
141
146
|
def extract_entities(text: str) -> List[str]:
|
|
142
147
|
"""Extract entities from text using Ollama graph-preflexor."""
|
|
143
148
|
try:
|
|
144
|
-
resp =
|
|
149
|
+
resp = _ollama_http.post(
|
|
145
150
|
f"{OLLAMA_URL}/api/generate",
|
|
146
151
|
json={
|
|
147
152
|
"model": "graph-preflexor",
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Regression test for the L6 _embed_client shadowing bug introduced in v0.8.0.
|
|
2
|
+
|
|
3
|
+
When the EmbedClient refactor landed in 0.8.0, the new `def _embed_client()`
|
|
4
|
+
factory function was added at module top, but the legacy module-level
|
|
5
|
+
`_embed_client = httpx.Client(timeout=60)` binding (used by Ollama entity
|
|
6
|
+
extraction) was left in place. Python's top-to-bottom evaluation rebound
|
|
7
|
+
the name to the httpx.Client instance, so any subsequent call to
|
|
8
|
+
`_embed_client()` raised `TypeError: 'Client' object is not callable`.
|
|
9
|
+
|
|
10
|
+
This silently 500'd every L6 /index-batch and /search request from 0.8.0
|
|
11
|
+
through 0.8.2 — the bug couldn't be caught by /health because the process
|
|
12
|
+
itself stays up, only the request handlers fail.
|
|
13
|
+
|
|
14
|
+
This is a static-source test (parses the file) rather than an import-time
|
|
15
|
+
test because L6's heavy imports (pymilvus, spacy) aren't available in the
|
|
16
|
+
unit-test venv. The check: scan the AST for any non-function rebinding of
|
|
17
|
+
identifiers that are also defined as `def` in the same module. Catches
|
|
18
|
+
this exact bug shape across any service that uses the EmbedClient factory
|
|
19
|
+
pattern.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import ast
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
import pytest
|
|
28
|
+
|
|
29
|
+
SERVICES_DIR = Path(__file__).parent.parent / "engine" / "services"
|
|
30
|
+
|
|
31
|
+
# Services that use the lazy EmbedClient factory pattern.
|
|
32
|
+
SERVICES_WITH_EMBED_FACTORY = [
|
|
33
|
+
SERVICES_DIR / "l4" / "server.py",
|
|
34
|
+
SERVICES_DIR / "l5" / "l5-comms-layer.py",
|
|
35
|
+
SERVICES_DIR / "l6" / "l6-document-store.py",
|
|
36
|
+
SERVICES_DIR / "l2" / "l2-hybridrag-proxy.py",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _module_level_defs_and_assigns(source: str) -> tuple[set[str], set[str]]:
|
|
41
|
+
"""Return (function names, non-function-assigned names) at module level."""
|
|
42
|
+
tree = ast.parse(source)
|
|
43
|
+
funcs: set[str] = set()
|
|
44
|
+
assigns: set[str] = set()
|
|
45
|
+
for node in tree.body:
|
|
46
|
+
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
47
|
+
funcs.add(node.name)
|
|
48
|
+
elif isinstance(node, ast.Assign):
|
|
49
|
+
for target in node.targets:
|
|
50
|
+
if isinstance(target, ast.Name):
|
|
51
|
+
assigns.add(target.id)
|
|
52
|
+
elif isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name):
|
|
53
|
+
assigns.add(node.target.id)
|
|
54
|
+
return funcs, assigns
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@pytest.mark.parametrize("service_file", SERVICES_WITH_EMBED_FACTORY, ids=lambda p: f"{p.parent.name}/{p.name}")
|
|
58
|
+
def test_no_module_level_shadowing_of_factory_functions(service_file: Path):
|
|
59
|
+
"""A module-level `def foo()` must not also have a module-level `foo = ...`
|
|
60
|
+
later in the file. That's exactly the shape that caused the v0.8.0 L6 bug."""
|
|
61
|
+
source = service_file.read_text()
|
|
62
|
+
funcs, assigns = _module_level_defs_and_assigns(source)
|
|
63
|
+
overlap = funcs & assigns
|
|
64
|
+
assert not overlap, (
|
|
65
|
+
f"{service_file.relative_to(SERVICES_DIR.parent.parent)} has module-level "
|
|
66
|
+
f"identifier(s) defined as both `def` and `name = ...`: {sorted(overlap)}. "
|
|
67
|
+
f"This causes silent name shadowing — the assignment wins and any call "
|
|
68
|
+
f"to {sorted(overlap)[0]}() raises TypeError at runtime."
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_l6_uses_renamed_ollama_http_not_embed_client():
|
|
73
|
+
"""Belt-and-suspenders: explicitly assert L6's Ollama HTTP client is at
|
|
74
|
+
`_ollama_http`, not `_embed_client`. If someone reintroduces the original
|
|
75
|
+
binding by accident, this test catches it without depending on AST traversal."""
|
|
76
|
+
source = (SERVICES_DIR / "l6" / "l6-document-store.py").read_text()
|
|
77
|
+
assert "_embed_client = httpx.Client" not in source, (
|
|
78
|
+
"L6 reintroduced the legacy `_embed_client = httpx.Client(...)` binding "
|
|
79
|
+
"that shadowed the EmbedClient factory in v0.8.0. Rename to _ollama_http."
|
|
80
|
+
)
|
|
81
|
+
assert "_ollama_http = httpx.Client" in source, (
|
|
82
|
+
"L6 is missing the renamed Ollama HTTP client (`_ollama_http = httpx.Client(...)`). "
|
|
83
|
+
"The Ollama entity-extraction call path needs an httpx.Client somewhere."
|
|
84
|
+
)
|