projectdavid 1.32.12__py3-none-any.whl → 1.32.14__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 +40 -64
- {projectdavid-1.32.12.dist-info → projectdavid-1.32.14.dist-info}/METADATA +1 -1
- {projectdavid-1.32.12.dist-info → projectdavid-1.32.14.dist-info}/RECORD +6 -6
- {projectdavid-1.32.12.dist-info → projectdavid-1.32.14.dist-info}/WHEEL +0 -0
- {projectdavid-1.32.12.dist-info → projectdavid-1.32.14.dist-info}/licenses/LICENSE +0 -0
- {projectdavid-1.32.12.dist-info → projectdavid-1.32.14.dist-info}/top_level.txt +0 -0
|
@@ -14,9 +14,7 @@ class SynchronousInferenceStream:
|
|
|
14
14
|
_GLOBAL_LOOP = asyncio.new_event_loop()
|
|
15
15
|
asyncio.set_event_loop(_GLOBAL_LOOP)
|
|
16
16
|
|
|
17
|
-
#
|
|
18
|
-
# ctor / setup
|
|
19
|
-
# --------------------------------------------------------------
|
|
17
|
+
# ────────────────────────────────────────────────────────────
|
|
20
18
|
def __init__(self, inference) -> None:
|
|
21
19
|
self.inference_client = inference
|
|
22
20
|
self.user_id: Optional[str] = None
|
|
@@ -26,6 +24,7 @@ class SynchronousInferenceStream:
|
|
|
26
24
|
self.run_id: Optional[str] = None
|
|
27
25
|
self.api_key: Optional[str] = None
|
|
28
26
|
|
|
27
|
+
# ────────────────────────────────────────────────────────────
|
|
29
28
|
def setup(
|
|
30
29
|
self,
|
|
31
30
|
user_id: str,
|
|
@@ -42,9 +41,7 @@ class SynchronousInferenceStream:
|
|
|
42
41
|
self.run_id = run_id
|
|
43
42
|
self.api_key = api_key
|
|
44
43
|
|
|
45
|
-
#
|
|
46
|
-
# main streaming entry-point
|
|
47
|
-
# --------------------------------------------------------------
|
|
44
|
+
# ────────────────────────────────────────────────────────────
|
|
48
45
|
def stream_chunks(
|
|
49
46
|
self,
|
|
50
47
|
provider: str,
|
|
@@ -71,60 +68,49 @@ class SynchronousInferenceStream:
|
|
|
71
68
|
|
|
72
69
|
agen = _stream_chunks_async().__aiter__()
|
|
73
70
|
|
|
74
|
-
#
|
|
71
|
+
# ── build the <fc> suppressor chain ─────────────────────────
|
|
75
72
|
if suppress_fc:
|
|
76
73
|
_suppressor = FunctionCallSuppressor()
|
|
77
74
|
_peek_gate = PeekGate(_suppressor)
|
|
78
75
|
|
|
79
|
-
def _filter_text(txt: str) -> str:
|
|
76
|
+
def _filter_text(txt: str) -> str: # noqa: D401
|
|
80
77
|
return _peek_gate.feed(txt)
|
|
81
78
|
|
|
82
79
|
else:
|
|
83
80
|
|
|
84
|
-
def _filter_text(txt: str) -> str: #
|
|
81
|
+
def _filter_text(txt: str) -> str: # noqa: D401
|
|
85
82
|
return txt
|
|
86
83
|
|
|
87
|
-
#
|
|
88
|
-
|
|
84
|
+
# helper – drain any leftover bytes still buffered
|
|
85
|
+
def _flush_tail() -> Optional[dict]:
|
|
86
|
+
"""
|
|
87
|
+
Force-flush PeekGate / FunctionCallSuppressor so the last few
|
|
88
|
+
characters (kept for safety-margin) reach the frontend.
|
|
89
|
+
Returns a ready-to-stream chunk or None.
|
|
90
|
+
"""
|
|
91
|
+
if suppress_fc:
|
|
92
|
+
tail = _filter_text("") # empty feed ⇒ flush
|
|
93
|
+
if tail:
|
|
94
|
+
return {"type": "content", "content": tail}
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
# ── main loop ─────────────────────────────────────────────
|
|
89
98
|
while True:
|
|
90
99
|
try:
|
|
91
100
|
chunk = self._GLOBAL_LOOP.run_until_complete(
|
|
92
101
|
asyncio.wait_for(agen.__anext__(), timeout=timeout_per_chunk)
|
|
93
102
|
)
|
|
94
103
|
|
|
95
|
-
# provider-labelled function_call
|
|
104
|
+
# ① drop provider-labelled function_call objects
|
|
96
105
|
if suppress_fc and chunk.get("type") == "function_call":
|
|
97
|
-
LOG.debug("[
|
|
98
|
-
continue
|
|
99
|
-
|
|
100
|
-
if suppress_fc and isinstance(chunk.get("content"), str):
|
|
101
|
-
chunk["content"] = _filter_text(chunk["content"])
|
|
102
|
-
if chunk["content"] == "":
|
|
103
|
-
# fully suppressed (either buffering or an <fc> block)
|
|
104
|
-
continue
|
|
105
|
-
|
|
106
|
-
# ------------------------------------------------------
|
|
107
|
-
# allow the assistants response1 to bypass suppression
|
|
108
|
-
# -------------------------------------------------------
|
|
109
|
-
if chunk.get("type") == "content":
|
|
110
|
-
yield chunk
|
|
111
|
-
continue
|
|
112
|
-
|
|
113
|
-
# -------------------------------------
|
|
114
|
-
# allow hot_code to bypass suppression
|
|
115
|
-
# ------------------------------------
|
|
116
|
-
if chunk.get("type") == "hot_code":
|
|
117
|
-
yield chunk
|
|
106
|
+
LOG.debug("[SUPPRESS] provider function_call dropped")
|
|
118
107
|
continue
|
|
119
108
|
|
|
120
|
-
#
|
|
121
|
-
if chunk.get("type")
|
|
109
|
+
# ② hot-code & file-preview payloads always pass
|
|
110
|
+
if chunk.get("type") in ("hot_code", "hot_code_output"):
|
|
122
111
|
yield chunk
|
|
123
112
|
continue
|
|
124
113
|
|
|
125
|
-
# -------------------------------------------------------
|
|
126
|
-
# allow code_interpreter_stream to bypass suppression
|
|
127
|
-
# --------------------------------------------------------
|
|
128
114
|
if (
|
|
129
115
|
chunk.get("stream_type") == "code_execution"
|
|
130
116
|
and chunk.get("chunk", {}).get("type") == "code_interpreter_stream"
|
|
@@ -132,45 +118,35 @@ class SynchronousInferenceStream:
|
|
|
132
118
|
yield chunk
|
|
133
119
|
continue
|
|
134
120
|
|
|
135
|
-
#
|
|
136
|
-
# inline content
|
|
137
|
-
# ----------------------------------
|
|
121
|
+
# ③ ordinary TEXT content — run through the <fc> filter
|
|
138
122
|
if isinstance(chunk.get("content"), str):
|
|
139
123
|
chunk["content"] = _filter_text(chunk["content"])
|
|
140
124
|
if chunk["content"] == "":
|
|
141
|
-
continue # fully suppressed
|
|
142
|
-
|
|
143
|
-
if (
|
|
144
|
-
suppress_fc
|
|
145
|
-
and '"name": "code_interpreter"' in chunk["content"]
|
|
146
|
-
and '"arguments": {"code"' in chunk["content"]
|
|
147
|
-
):
|
|
148
|
-
LOG.debug("[SUPPRESSOR] inline code_interpreter match blocked")
|
|
149
|
-
continue
|
|
150
|
-
|
|
151
|
-
if (
|
|
152
|
-
suppress_fc
|
|
153
|
-
and '"name": "file_search"' in chunk["content"]
|
|
154
|
-
and '"arguments": {"query_text"' in chunk["content"]
|
|
155
|
-
):
|
|
156
|
-
LOG.debug("[SUPPRESSOR] inline file_search match blocked")
|
|
157
|
-
continue
|
|
125
|
+
continue # fully suppressed / still buffering
|
|
158
126
|
|
|
127
|
+
# ④ everything else streams unchanged
|
|
159
128
|
yield chunk
|
|
160
129
|
|
|
130
|
+
# ─────────── graceful endings ───────────
|
|
161
131
|
except StopAsyncIteration:
|
|
132
|
+
if tail := _flush_tail():
|
|
133
|
+
yield tail
|
|
162
134
|
LOG.info("Stream completed normally.")
|
|
163
135
|
break
|
|
136
|
+
|
|
164
137
|
except asyncio.TimeoutError:
|
|
165
|
-
|
|
138
|
+
if tail := _flush_tail():
|
|
139
|
+
yield tail
|
|
140
|
+
LOG.error("[TimeoutError] Chunk wait expired – aborting stream.")
|
|
166
141
|
break
|
|
167
|
-
|
|
168
|
-
|
|
142
|
+
|
|
143
|
+
except Exception as exc: # noqa: BLE001
|
|
144
|
+
if tail := _flush_tail():
|
|
145
|
+
yield tail
|
|
146
|
+
LOG.error("Unexpected streaming error: %s", exc, exc_info=True)
|
|
169
147
|
break
|
|
170
148
|
|
|
171
|
-
#
|
|
172
|
-
# housekeeping
|
|
173
|
-
# --------------------------------------------------------------
|
|
149
|
+
# ────────────────────────────────────────────────────────────
|
|
174
150
|
@classmethod
|
|
175
151
|
def shutdown_loop(cls) -> None:
|
|
176
152
|
if cls._GLOBAL_LOOP and not cls._GLOBAL_LOOP.is_closed():
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: projectdavid
|
|
3
|
-
Version: 1.32.
|
|
3
|
+
Version: 1.32.14
|
|
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=RuFOgvUar1p8xpklMZTMswDjZ8n2N3ghtjmDjSiI6Eo,6209
|
|
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
|
|
@@ -34,8 +34,8 @@ projectdavid/utils/monitor_launcher.py,sha256=3YAgJdeuaUvq3JGvpA4ymqFsAnk29nH5q9
|
|
|
34
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.14.dist-info/licenses/LICENSE,sha256=_8yjiEGttpS284BkfhXxfERqTRZW_tUaHiBB0GTJTMg,4563
|
|
38
|
+
projectdavid-1.32.14.dist-info/METADATA,sha256=lwrij-9rEDGMo6elHx8cZyAmArJirSaL1Nit8177LeA,10782
|
|
39
|
+
projectdavid-1.32.14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
40
|
+
projectdavid-1.32.14.dist-info/top_level.txt,sha256=kil8GU4s7qYRfNnzGnFHhZnSNRSxgNG-J4HLgQMmMtw,13
|
|
41
|
+
projectdavid-1.32.14.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|