projectdavid 1.32.0__py3-none-any.whl → 1.32.2__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.
Potentially problematic release.
This version of projectdavid might be problematic. Click here for more details.
- projectdavid/clients/synchronous_inference_wrapper.py +17 -13
- projectdavid/utils/function_call_suppressor.py +6 -7
- projectdavid/utils/peek_gate.py +36 -14
- {projectdavid-1.32.0.dist-info → projectdavid-1.32.2.dist-info}/METADATA +1 -1
- {projectdavid-1.32.0.dist-info → projectdavid-1.32.2.dist-info}/RECORD +8 -8
- {projectdavid-1.32.0.dist-info → projectdavid-1.32.2.dist-info}/WHEEL +0 -0
- {projectdavid-1.32.0.dist-info → projectdavid-1.32.2.dist-info}/licenses/LICENSE +0 -0
- {projectdavid-1.32.0.dist-info → projectdavid-1.32.2.dist-info}/top_level.txt +0 -0
|
@@ -49,15 +49,14 @@ class SynchronousInferenceStream:
|
|
|
49
49
|
self,
|
|
50
50
|
provider: str,
|
|
51
51
|
model: str,
|
|
52
|
-
*,
|
|
52
|
+
*,
|
|
53
53
|
api_key: Optional[str] = None,
|
|
54
54
|
timeout_per_chunk: float = 280.0,
|
|
55
|
-
suppress_fc: bool = True,
|
|
55
|
+
suppress_fc: bool = True,
|
|
56
56
|
) -> Generator[dict, None, None]:
|
|
57
57
|
|
|
58
58
|
resolved_api_key = api_key or self.api_key
|
|
59
59
|
|
|
60
|
-
# ------------- build async generator ----------------------
|
|
61
60
|
async def _stream_chunks_async():
|
|
62
61
|
async for chk in self.inference_client.stream_inference_response(
|
|
63
62
|
provider=provider,
|
|
@@ -72,10 +71,10 @@ class SynchronousInferenceStream:
|
|
|
72
71
|
|
|
73
72
|
agen = _stream_chunks_async().__aiter__()
|
|
74
73
|
|
|
75
|
-
#
|
|
74
|
+
# ---------- suppression chain ----------
|
|
76
75
|
if suppress_fc:
|
|
77
76
|
_suppressor = FunctionCallSuppressor()
|
|
78
|
-
_peek_gate = PeekGate(_suppressor)
|
|
77
|
+
_peek_gate = PeekGate(_suppressor)
|
|
79
78
|
|
|
80
79
|
def _filter_text(txt: str) -> str:
|
|
81
80
|
return _peek_gate.feed(txt)
|
|
@@ -85,7 +84,7 @@ class SynchronousInferenceStream:
|
|
|
85
84
|
def _filter_text(txt: str) -> str: # no-op
|
|
86
85
|
return txt
|
|
87
86
|
|
|
88
|
-
#
|
|
87
|
+
# ---------------------------------------
|
|
89
88
|
|
|
90
89
|
while True:
|
|
91
90
|
try:
|
|
@@ -93,18 +92,23 @@ class SynchronousInferenceStream:
|
|
|
93
92
|
asyncio.wait_for(agen.__anext__(), timeout=timeout_per_chunk)
|
|
94
93
|
)
|
|
95
94
|
|
|
96
|
-
#
|
|
95
|
+
# provider-labelled function_call
|
|
97
96
|
if suppress_fc and chunk.get("type") == "function_call":
|
|
98
|
-
LOG.debug(
|
|
99
|
-
"[SUPPRESSOR] blocked provider-labelled function_call chunk"
|
|
100
|
-
)
|
|
97
|
+
LOG.debug("[SUPPRESSOR] blocked provider-labelled function_call")
|
|
101
98
|
continue
|
|
102
99
|
|
|
103
|
-
#
|
|
100
|
+
# inline content
|
|
104
101
|
if isinstance(chunk.get("content"), str):
|
|
105
102
|
chunk["content"] = _filter_text(chunk["content"])
|
|
106
|
-
if
|
|
107
|
-
# fully suppressed
|
|
103
|
+
if chunk["content"] == "":
|
|
104
|
+
continue # fully suppressed (or still peeking)
|
|
105
|
+
|
|
106
|
+
# additional raw inline suppression for partial JSON
|
|
107
|
+
if (
|
|
108
|
+
'"name": "code_interpreter"' in chunk["content"]
|
|
109
|
+
and '"arguments": {"code"' in chunk["content"]
|
|
110
|
+
):
|
|
111
|
+
LOG.debug("[SUPPRESSOR] inline code_interpreter match blocked")
|
|
108
112
|
continue
|
|
109
113
|
|
|
110
114
|
yield chunk
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
# ------------------------------------------------------------------
|
|
2
|
+
# utils.function_call_suppressor (unchanged except for logger)
|
|
3
|
+
# ------------------------------------------------------------------
|
|
1
4
|
import re
|
|
2
5
|
|
|
3
6
|
from projectdavid_common.utilities.logging_service import LoggingUtility
|
|
@@ -5,9 +8,6 @@ from projectdavid_common.utilities.logging_service import LoggingUtility
|
|
|
5
8
|
LOG = LoggingUtility()
|
|
6
9
|
|
|
7
10
|
|
|
8
|
-
# ─────────────────────────────────────────────────────────────────────
|
|
9
|
-
# function-call filter helpers (unchanged except for logger)
|
|
10
|
-
# ─────────────────────────────────────────────────────────────────────
|
|
11
11
|
class FunctionCallSuppressor:
|
|
12
12
|
OPEN_RE = re.compile(r"<\s*fc\s*>", re.I)
|
|
13
13
|
CLOSE_RE = re.compile(r"</\s*fc\s*>", re.I)
|
|
@@ -16,10 +16,9 @@ class FunctionCallSuppressor:
|
|
|
16
16
|
self.in_fc = False
|
|
17
17
|
self.buf = ""
|
|
18
18
|
|
|
19
|
-
def filter_chunk(self,
|
|
20
|
-
self.buf +=
|
|
19
|
+
def filter_chunk(self, txt: str) -> str:
|
|
20
|
+
self.buf += txt
|
|
21
21
|
out = ""
|
|
22
|
-
|
|
23
22
|
while self.buf:
|
|
24
23
|
if not self.in_fc:
|
|
25
24
|
m = self.OPEN_RE.search(self.buf)
|
|
@@ -34,7 +33,7 @@ class FunctionCallSuppressor:
|
|
|
34
33
|
else:
|
|
35
34
|
m = self.CLOSE_RE.search(self.buf)
|
|
36
35
|
if not m:
|
|
37
|
-
break
|
|
36
|
+
break
|
|
38
37
|
LOG.debug("[SUPPRESSOR] </fc> detected — block suppressed")
|
|
39
38
|
self.buf = self.buf[m.end() :]
|
|
40
39
|
self.in_fc = False
|
projectdavid/utils/peek_gate.py
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
# ------------------------------------------------------------------
|
|
2
|
+
# utils.peek_gate (progressive flush version)
|
|
3
|
+
# ------------------------------------------------------------------
|
|
1
4
|
import re
|
|
2
5
|
|
|
3
6
|
from projectdavid_common.utilities.logging_service import LoggingUtility
|
|
@@ -9,17 +12,35 @@ LOG = LoggingUtility()
|
|
|
9
12
|
|
|
10
13
|
class PeekGate:
|
|
11
14
|
"""
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
• Streams *most* text immediately while the gate decides whether
|
|
16
|
+
a <fc> block will appear.
|
|
17
|
+
• Keeps only SAFETY_MARGIN bytes back so a tag split across two
|
|
18
|
+
chunks can still be caught.
|
|
14
19
|
"""
|
|
15
20
|
|
|
21
|
+
SAFETY_MARGIN = 8 # enough to hold "<fc>" + whitespace
|
|
22
|
+
|
|
16
23
|
def __init__(self, downstream: FunctionCallSuppressor, peek_limit: int = 2048):
|
|
17
24
|
self.downstream = downstream
|
|
18
25
|
self.peek_limit = peek_limit
|
|
19
26
|
self.buf = ""
|
|
20
|
-
self.mode = "peeking" #
|
|
27
|
+
self.mode = "peeking" # → "normal" after decision
|
|
21
28
|
self.suppressing = False
|
|
22
29
|
|
|
30
|
+
# ----------------------------------------------------------
|
|
31
|
+
def _safe_flush(self, new_txt: str = "") -> str:
|
|
32
|
+
"""
|
|
33
|
+
Return everything except the last SAFETY_MARGIN chars,
|
|
34
|
+
which are kept for tag-boundary safety.
|
|
35
|
+
"""
|
|
36
|
+
self.buf += new_txt
|
|
37
|
+
if len(self.buf) <= self.SAFETY_MARGIN:
|
|
38
|
+
return ""
|
|
39
|
+
flush_len = len(self.buf) - self.SAFETY_MARGIN
|
|
40
|
+
head, self.buf = self.buf[:flush_len], self.buf[flush_len:]
|
|
41
|
+
return head
|
|
42
|
+
|
|
43
|
+
# ----------------------------------------------------------
|
|
23
44
|
def feed(self, txt: str) -> str:
|
|
24
45
|
# decision already taken
|
|
25
46
|
if self.mode == "normal":
|
|
@@ -28,22 +49,23 @@ class PeekGate:
|
|
|
28
49
|
# still peeking …
|
|
29
50
|
self.buf += txt
|
|
30
51
|
m = re.search(r"<\s*fc\s*>", self.buf, flags=re.I)
|
|
31
|
-
if m: # found
|
|
52
|
+
if m: # tag found
|
|
32
53
|
head = self.buf[: m.start()]
|
|
33
|
-
LOG.debug("[PEEK] <fc> located
|
|
54
|
+
LOG.debug("[PEEK] <fc> located – engaging suppressor")
|
|
34
55
|
self.suppressing = True
|
|
35
56
|
self.mode = "normal"
|
|
36
|
-
tail = self.buf[m.start() :]
|
|
37
|
-
self.buf = ""
|
|
57
|
+
tail, self.buf = self.buf[m.start() :], ""
|
|
38
58
|
return head + self.downstream.filter_chunk(tail)
|
|
39
59
|
|
|
40
|
-
|
|
60
|
+
# no tag yet – overflow?
|
|
61
|
+
if len(self.buf) >= self.peek_limit:
|
|
41
62
|
LOG.debug(
|
|
42
|
-
"[PEEK] no <fc> tag within first %d chars –
|
|
63
|
+
"[PEEK] no <fc> tag within first %d chars – " "streaming normally",
|
|
43
64
|
self.peek_limit,
|
|
44
65
|
)
|
|
45
|
-
self.mode = "normal"
|
|
46
|
-
self.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
66
|
+
flush, self.mode = self.buf, "normal"
|
|
67
|
+
self.buf = ""
|
|
68
|
+
return flush
|
|
69
|
+
|
|
70
|
+
# still undecided → flush safe part
|
|
71
|
+
return self._safe_flush()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: projectdavid
|
|
3
|
-
Version: 1.32.
|
|
3
|
+
Version: 1.32.2
|
|
4
4
|
Summary: Python SDK for interacting with the Entities Assistant API.
|
|
5
5
|
Author-email: Francis Neequaye Armah <francis.neequaye@projectdavid.co.uk>
|
|
6
6
|
License: PolyForm Noncommercial License 1.0.0
|
|
@@ -15,7 +15,7 @@ projectdavid/clients/files_client.py,sha256=XkIDzbQFGDrd88taf0Kouc_4YJOPIYEHiIyW
|
|
|
15
15
|
projectdavid/clients/inference_client.py,sha256=xz4ACPv5Tkis604QxO5mJX1inH_TGDfQP-31geETYpE,6609
|
|
16
16
|
projectdavid/clients/messages_client.py,sha256=467xeIt3VYs6cG8-bl-eDRi_auWOPmfd5tSJDmQSJUI,17232
|
|
17
17
|
projectdavid/clients/runs.py,sha256=-fXOq5L9w2efDPmZkNxb0s2yjl6oN0XN4_aLXqaeceo,25270
|
|
18
|
-
projectdavid/clients/synchronous_inference_wrapper.py,sha256=
|
|
18
|
+
projectdavid/clients/synchronous_inference_wrapper.py,sha256=Rv7TGZBnzMOBDw3BwguDOHcVmwLvcjHp7j6V8OAqpwM,4686
|
|
19
19
|
projectdavid/clients/threads_client.py,sha256=ekzU5w14zftmtmFkiec3NC90Of-_KVSUY1qH9cmfSFg,6771
|
|
20
20
|
projectdavid/clients/tools_client.py,sha256=GkCVOmwpAoPqVt6aYmH0G1HIFha3iEwR9IIf9teR0j8,11487
|
|
21
21
|
projectdavid/clients/users_client.py,sha256=eCuUb9qvyH1GUFhZu6TRL9zdoK-qzHSs8-Vmrk_0mmg,13729
|
|
@@ -29,13 +29,13 @@ projectdavid/synthesis/prompt.py,sha256=Jlya6avnWV5ryaJaibt4W29XYxRlJRV6rwHfjgYy
|
|
|
29
29
|
projectdavid/synthesis/reranker.py,sha256=aFjdLDoqnhgzxPIoRfs4y_Z_F6T88g5Sg8R5m0HMxdU,868
|
|
30
30
|
projectdavid/synthesis/retriever.py,sha256=8R5HIgPXMKIQxV5KsN8u_gVVP4JY-wsDwJd92A-74-o,2300
|
|
31
31
|
projectdavid/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
|
-
projectdavid/utils/function_call_suppressor.py,sha256=
|
|
32
|
+
projectdavid/utils/function_call_suppressor.py,sha256=LsDhySVH0kS8nydPBrHrOtT30yhVvwU24QHApGu1P34,1305
|
|
33
33
|
projectdavid/utils/monitor_launcher.py,sha256=3YAgJdeuaUvq3JGvpA4ymqFsAnk29nH5q93cwStP4hc,2836
|
|
34
|
-
projectdavid/utils/peek_gate.py,sha256=
|
|
34
|
+
projectdavid/utils/peek_gate.py,sha256=5whMRnDOQjATRpThWDJkvY9ScXuJ7Sd_-9rvGgXeTAQ,2532
|
|
35
35
|
projectdavid/utils/run_monitor.py,sha256=F_WkqIP-qnWH-4llIbileWWLfRj2Q1Cg-ni23SR1rec,3786
|
|
36
36
|
projectdavid/utils/vector_search_formatter.py,sha256=YTe3HPGec26qGY7uxY8_GS8lc4QaN6aNXMzkl29nZpI,1735
|
|
37
|
-
projectdavid-1.32.
|
|
38
|
-
projectdavid-1.32.
|
|
39
|
-
projectdavid-1.32.
|
|
40
|
-
projectdavid-1.32.
|
|
41
|
-
projectdavid-1.32.
|
|
37
|
+
projectdavid-1.32.2.dist-info/licenses/LICENSE,sha256=_8yjiEGttpS284BkfhXxfERqTRZW_tUaHiBB0GTJTMg,4563
|
|
38
|
+
projectdavid-1.32.2.dist-info/METADATA,sha256=FS7ZUJmxyRntFkY4FQjaDPXkqihI8i6yzwpYFC_N2rI,10781
|
|
39
|
+
projectdavid-1.32.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
40
|
+
projectdavid-1.32.2.dist-info/top_level.txt,sha256=kil8GU4s7qYRfNnzGnFHhZnSNRSxgNG-J4HLgQMmMtw,13
|
|
41
|
+
projectdavid-1.32.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|