projectdavid 1.31.0__py3-none-any.whl → 1.32.0__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/file_processor.py +30 -80
- projectdavid/clients/synchronous_inference_wrapper.py +56 -27
- projectdavid/utils/function_call_suppressor.py +41 -0
- projectdavid/utils/peek_gate.py +49 -0
- {projectdavid-1.31.0.dist-info → projectdavid-1.32.0.dist-info}/METADATA +1 -2
- {projectdavid-1.31.0.dist-info → projectdavid-1.32.0.dist-info}/RECORD +9 -7
- {projectdavid-1.31.0.dist-info → projectdavid-1.32.0.dist-info}/WHEEL +1 -1
- {projectdavid-1.31.0.dist-info → projectdavid-1.32.0.dist-info}/licenses/LICENSE +0 -0
- {projectdavid-1.31.0.dist-info → projectdavid-1.32.0.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import csv
|
|
3
3
|
import json
|
|
4
|
-
import mimetypes
|
|
5
4
|
import re
|
|
6
5
|
import textwrap
|
|
7
6
|
from concurrent.futures import ThreadPoolExecutor
|
|
@@ -10,10 +9,9 @@ from typing import Any, Dict, List, Tuple, Union
|
|
|
10
9
|
|
|
11
10
|
try: # Python 3.11+
|
|
12
11
|
from typing import LiteralString
|
|
13
|
-
except ImportError: # 3.9
|
|
12
|
+
except ImportError: # 3.9–3.10
|
|
14
13
|
from typing_extensions import LiteralString
|
|
15
14
|
|
|
16
|
-
import magic
|
|
17
15
|
import numpy as np
|
|
18
16
|
import pdfplumber
|
|
19
17
|
from docx import Document
|
|
@@ -54,54 +52,30 @@ class FileProcessor:
|
|
|
54
52
|
raise ValueError(f"{file_path.name} > {mb} MB limit")
|
|
55
53
|
|
|
56
54
|
# ------------------------------------------------------------------ #
|
|
57
|
-
# File-type detection
|
|
55
|
+
# File-type detection (simple extension map – NO libmagic)
|
|
58
56
|
# ------------------------------------------------------------------ #
|
|
59
57
|
def _detect_file_type(self, file_path: Path) -> str:
|
|
60
58
|
"""
|
|
61
|
-
Return
|
|
59
|
+
Return one of:
|
|
62
60
|
|
|
63
|
-
• 'pdf'
|
|
64
|
-
• '
|
|
65
|
-
• 'text'
|
|
61
|
+
• 'pdf' • 'csv' • 'json'
|
|
62
|
+
• 'office' (.doc/.docx/.pptx)
|
|
63
|
+
• 'text' (code / markup / plain text)
|
|
66
64
|
|
|
67
|
-
Raises *ValueError*
|
|
65
|
+
Raises *ValueError* if the extension is not recognised.
|
|
68
66
|
"""
|
|
69
|
-
# 1️⃣ Best-effort MIME sniff
|
|
70
|
-
mime_type: str | None = None
|
|
71
|
-
if magic is not None:
|
|
72
|
-
try:
|
|
73
|
-
mime_type = magic.from_file(str(file_path), mime=True)
|
|
74
|
-
except Exception:
|
|
75
|
-
mime_type = None
|
|
76
|
-
|
|
77
|
-
# 2️⃣ Fallback → mimetypes
|
|
78
|
-
if not mime_type:
|
|
79
|
-
mime_type, _ = mimetypes.guess_type(file_path.name)
|
|
80
|
-
|
|
81
67
|
suffix = file_path.suffix.lower()
|
|
82
68
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
"
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
"text/markdown",
|
|
94
|
-
"text/x-python",
|
|
95
|
-
"text/x-c",
|
|
96
|
-
"text/x-c++",
|
|
97
|
-
"text/x-java-source",
|
|
98
|
-
"text/x-script.python",
|
|
99
|
-
"text/html",
|
|
100
|
-
"text/css",
|
|
101
|
-
"application/typescript",
|
|
102
|
-
"text/javascript",
|
|
103
|
-
}
|
|
104
|
-
TEXT_EXTS = {
|
|
69
|
+
if suffix == ".pdf":
|
|
70
|
+
return "pdf"
|
|
71
|
+
if suffix == ".csv":
|
|
72
|
+
return "csv"
|
|
73
|
+
if suffix == ".json":
|
|
74
|
+
return "json"
|
|
75
|
+
if suffix in {".doc", ".docx", ".pptx"}:
|
|
76
|
+
return "office"
|
|
77
|
+
|
|
78
|
+
text_exts = {
|
|
105
79
|
".txt",
|
|
106
80
|
".md",
|
|
107
81
|
".rst",
|
|
@@ -120,32 +94,10 @@ class FileProcessor:
|
|
|
120
94
|
".html",
|
|
121
95
|
".css",
|
|
122
96
|
}
|
|
123
|
-
|
|
124
|
-
# --- PDF ---
|
|
125
|
-
if mime_type in PDF_MIMES or suffix == ".pdf":
|
|
126
|
-
return "pdf"
|
|
127
|
-
|
|
128
|
-
# --- CSV ---
|
|
129
|
-
if mime_type in CSV_MIMES or suffix == ".csv":
|
|
130
|
-
return "csv"
|
|
131
|
-
|
|
132
|
-
# --- JSON ---
|
|
133
|
-
if mime_type in JSON_MIMES or suffix == ".json":
|
|
134
|
-
return "json"
|
|
135
|
-
|
|
136
|
-
# --- Office documents ---
|
|
137
|
-
if mime_type in OFFICE_MIMES or suffix in {".doc", ".docx", ".pptx"}:
|
|
138
|
-
return "office"
|
|
139
|
-
|
|
140
|
-
# --- Generic text / code / markup ---
|
|
141
|
-
if mime_type in TEXT_MIMES or suffix in TEXT_EXTS:
|
|
97
|
+
if suffix in text_exts:
|
|
142
98
|
return "text"
|
|
143
99
|
|
|
144
|
-
|
|
145
|
-
raise ValueError(
|
|
146
|
-
f"Unsupported file type for '{file_path.name}': "
|
|
147
|
-
f"MIME={mime_type or 'unknown'} extension={suffix}"
|
|
148
|
-
)
|
|
100
|
+
raise ValueError(f"Unsupported file type: {file_path.name} (ext={suffix})")
|
|
149
101
|
|
|
150
102
|
# ------------------------------------------------------------------ #
|
|
151
103
|
# Public entry-point
|
|
@@ -156,19 +108,17 @@ class FileProcessor:
|
|
|
156
108
|
self.validate_file(file_path)
|
|
157
109
|
ftype = self._detect_file_type(file_path)
|
|
158
110
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
# Safety net (should never hit)
|
|
171
|
-
raise ValueError(f"Unsupported file type: {file_path.suffix}")
|
|
111
|
+
dispatch_map = {
|
|
112
|
+
"pdf": self._process_pdf,
|
|
113
|
+
"text": self._process_text,
|
|
114
|
+
"csv": self._process_csv,
|
|
115
|
+
"office": self._process_office,
|
|
116
|
+
"json": self._process_json,
|
|
117
|
+
}
|
|
118
|
+
if ftype not in dispatch_map:
|
|
119
|
+
raise ValueError(f"Unsupported file type: {file_path.suffix}")
|
|
120
|
+
|
|
121
|
+
return await dispatch_map[ftype](file_path)
|
|
172
122
|
|
|
173
123
|
# ------------------------------------------------------------------ #
|
|
174
124
|
# PDF
|
|
@@ -4,13 +4,19 @@ from typing import Generator, Optional
|
|
|
4
4
|
|
|
5
5
|
from projectdavid_common import UtilsInterface
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
from projectdavid.utils.function_call_suppressor import FunctionCallSuppressor
|
|
8
|
+
from projectdavid.utils.peek_gate import PeekGate
|
|
9
|
+
|
|
10
|
+
LOG = UtilsInterface.LoggingUtility()
|
|
8
11
|
|
|
9
12
|
|
|
10
13
|
class SynchronousInferenceStream:
|
|
11
14
|
_GLOBAL_LOOP = asyncio.new_event_loop()
|
|
12
15
|
asyncio.set_event_loop(_GLOBAL_LOOP)
|
|
13
16
|
|
|
17
|
+
# --------------------------------------------------------------
|
|
18
|
+
# ctor / setup
|
|
19
|
+
# --------------------------------------------------------------
|
|
14
20
|
def __init__(self, inference) -> None:
|
|
15
21
|
self.inference_client = inference
|
|
16
22
|
self.user_id: Optional[str] = None
|
|
@@ -36,31 +42,24 @@ class SynchronousInferenceStream:
|
|
|
36
42
|
self.run_id = run_id
|
|
37
43
|
self.api_key = api_key
|
|
38
44
|
|
|
45
|
+
# --------------------------------------------------------------
|
|
46
|
+
# main streaming entry-point
|
|
47
|
+
# --------------------------------------------------------------
|
|
39
48
|
def stream_chunks(
|
|
40
49
|
self,
|
|
41
50
|
provider: str,
|
|
42
51
|
model: str,
|
|
43
|
-
*, #
|
|
52
|
+
*, # keyword-only from here
|
|
44
53
|
api_key: Optional[str] = None,
|
|
45
54
|
timeout_per_chunk: float = 280.0,
|
|
55
|
+
suppress_fc: bool = True, # ← NEW switch
|
|
46
56
|
) -> Generator[dict, None, None]:
|
|
47
|
-
|
|
48
|
-
Streams inference response chunks synchronously by wrapping an async generator.
|
|
49
|
-
|
|
50
|
-
Args:
|
|
51
|
-
provider (str): The provider name.
|
|
52
|
-
model (str): The model name.
|
|
53
|
-
api_key (Optional[str]): API key for authentication. If not provided, falls back to the value from setup().
|
|
54
|
-
timeout_per_chunk (float): Timeout per chunk in seconds.
|
|
55
|
-
|
|
56
|
-
Yields:
|
|
57
|
-
dict: A chunk of the inference response.
|
|
58
|
-
"""
|
|
59
|
-
# Prefer direct input over self.api_key
|
|
57
|
+
|
|
60
58
|
resolved_api_key = api_key or self.api_key
|
|
61
59
|
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
# ------------- build async generator ----------------------
|
|
61
|
+
async def _stream_chunks_async():
|
|
62
|
+
async for chk in self.inference_client.stream_inference_response(
|
|
64
63
|
provider=provider,
|
|
65
64
|
model=model,
|
|
66
65
|
api_key=resolved_api_key,
|
|
@@ -69,30 +68,60 @@ class SynchronousInferenceStream:
|
|
|
69
68
|
run_id=self.run_id,
|
|
70
69
|
assistant_id=self.assistant_id,
|
|
71
70
|
):
|
|
72
|
-
yield
|
|
71
|
+
yield chk
|
|
72
|
+
|
|
73
|
+
agen = _stream_chunks_async().__aiter__()
|
|
74
|
+
|
|
75
|
+
# ------------- optional suppression chain -----------------
|
|
76
|
+
if suppress_fc:
|
|
77
|
+
_suppressor = FunctionCallSuppressor()
|
|
78
|
+
_peek_gate = PeekGate(_suppressor) # 2 KB default
|
|
79
|
+
|
|
80
|
+
def _filter_text(txt: str) -> str:
|
|
81
|
+
return _peek_gate.feed(txt)
|
|
82
|
+
|
|
83
|
+
else:
|
|
73
84
|
|
|
74
|
-
|
|
85
|
+
def _filter_text(txt: str) -> str: # no-op
|
|
86
|
+
return txt
|
|
87
|
+
|
|
88
|
+
# ----------------------------------------------------------
|
|
75
89
|
|
|
76
90
|
while True:
|
|
77
91
|
try:
|
|
78
92
|
chunk = self._GLOBAL_LOOP.run_until_complete(
|
|
79
|
-
asyncio.wait_for(
|
|
93
|
+
asyncio.wait_for(agen.__anext__(), timeout=timeout_per_chunk)
|
|
80
94
|
)
|
|
95
|
+
|
|
96
|
+
# --- provider-labelled function_call blocks -------
|
|
97
|
+
if suppress_fc and chunk.get("type") == "function_call":
|
|
98
|
+
LOG.debug(
|
|
99
|
+
"[SUPPRESSOR] blocked provider-labelled function_call chunk"
|
|
100
|
+
)
|
|
101
|
+
continue
|
|
102
|
+
|
|
103
|
+
# --- inline content filtering --------------------
|
|
104
|
+
if isinstance(chunk.get("content"), str):
|
|
105
|
+
chunk["content"] = _filter_text(chunk["content"])
|
|
106
|
+
if not chunk["content"]:
|
|
107
|
+
# fully suppressed → skip forwarding
|
|
108
|
+
continue
|
|
109
|
+
|
|
81
110
|
yield chunk
|
|
111
|
+
|
|
82
112
|
except StopAsyncIteration:
|
|
83
|
-
|
|
113
|
+
LOG.info("Stream completed normally.")
|
|
84
114
|
break
|
|
85
115
|
except asyncio.TimeoutError:
|
|
86
|
-
|
|
87
|
-
"[TimeoutError] Timeout occurred, stopping stream."
|
|
88
|
-
)
|
|
116
|
+
LOG.error("[TimeoutError] Timeout occurred, stopping stream.")
|
|
89
117
|
break
|
|
90
118
|
except Exception as e:
|
|
91
|
-
|
|
92
|
-
"Unexpected error during streaming completions: %s", e
|
|
93
|
-
)
|
|
119
|
+
LOG.error("Unexpected error during streaming completions: %s", e)
|
|
94
120
|
break
|
|
95
121
|
|
|
122
|
+
# --------------------------------------------------------------
|
|
123
|
+
# housekeeping
|
|
124
|
+
# --------------------------------------------------------------
|
|
96
125
|
@classmethod
|
|
97
126
|
def shutdown_loop(cls) -> None:
|
|
98
127
|
if cls._GLOBAL_LOOP and not cls._GLOBAL_LOOP.is_closed():
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from projectdavid_common.utilities.logging_service import LoggingUtility
|
|
4
|
+
|
|
5
|
+
LOG = LoggingUtility()
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# ─────────────────────────────────────────────────────────────────────
|
|
9
|
+
# function-call filter helpers (unchanged except for logger)
|
|
10
|
+
# ─────────────────────────────────────────────────────────────────────
|
|
11
|
+
class FunctionCallSuppressor:
|
|
12
|
+
OPEN_RE = re.compile(r"<\s*fc\s*>", re.I)
|
|
13
|
+
CLOSE_RE = re.compile(r"</\s*fc\s*>", re.I)
|
|
14
|
+
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self.in_fc = False
|
|
17
|
+
self.buf = ""
|
|
18
|
+
|
|
19
|
+
def filter_chunk(self, chunk: str) -> str:
|
|
20
|
+
self.buf += chunk
|
|
21
|
+
out = ""
|
|
22
|
+
|
|
23
|
+
while self.buf:
|
|
24
|
+
if not self.in_fc:
|
|
25
|
+
m = self.OPEN_RE.search(self.buf)
|
|
26
|
+
if not m:
|
|
27
|
+
out += self.buf
|
|
28
|
+
self.buf = ""
|
|
29
|
+
break
|
|
30
|
+
out += self.buf[: m.start()]
|
|
31
|
+
LOG.debug("[SUPPRESSOR] <fc> detected")
|
|
32
|
+
self.buf = self.buf[m.end() :]
|
|
33
|
+
self.in_fc = True
|
|
34
|
+
else:
|
|
35
|
+
m = self.CLOSE_RE.search(self.buf)
|
|
36
|
+
if not m:
|
|
37
|
+
break # wait for more tokens
|
|
38
|
+
LOG.debug("[SUPPRESSOR] </fc> detected — block suppressed")
|
|
39
|
+
self.buf = self.buf[m.end() :]
|
|
40
|
+
self.in_fc = False
|
|
41
|
+
return out
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from projectdavid_common.utilities.logging_service import LoggingUtility
|
|
4
|
+
|
|
5
|
+
from .function_call_suppressor import FunctionCallSuppressor
|
|
6
|
+
|
|
7
|
+
LOG = LoggingUtility()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PeekGate:
|
|
11
|
+
"""
|
|
12
|
+
Turns the suppressor on only if a <fc> block appears in the first
|
|
13
|
+
`peek_limit` characters; otherwise passes text through unchanged.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, downstream: FunctionCallSuppressor, peek_limit: int = 2048):
|
|
17
|
+
self.downstream = downstream
|
|
18
|
+
self.peek_limit = peek_limit
|
|
19
|
+
self.buf = ""
|
|
20
|
+
self.mode = "peeking" # -> "normal" after decision
|
|
21
|
+
self.suppressing = False
|
|
22
|
+
|
|
23
|
+
def feed(self, txt: str) -> str:
|
|
24
|
+
# decision already taken
|
|
25
|
+
if self.mode == "normal":
|
|
26
|
+
return self.downstream.filter_chunk(txt) if self.suppressing else txt
|
|
27
|
+
|
|
28
|
+
# still peeking …
|
|
29
|
+
self.buf += txt
|
|
30
|
+
m = re.search(r"<\s*fc\s*>", self.buf, flags=re.I)
|
|
31
|
+
if m: # found a tag
|
|
32
|
+
head = self.buf[: m.start()]
|
|
33
|
+
LOG.debug("[PEEK] <fc> located after leading text – engaging suppressor")
|
|
34
|
+
self.suppressing = True
|
|
35
|
+
self.mode = "normal"
|
|
36
|
+
tail = self.buf[m.start() :]
|
|
37
|
+
self.buf = ""
|
|
38
|
+
return head + self.downstream.filter_chunk(tail)
|
|
39
|
+
|
|
40
|
+
if len(self.buf) >= self.peek_limit: # give up
|
|
41
|
+
LOG.debug(
|
|
42
|
+
"[PEEK] no <fc> tag within first %d chars – no suppression",
|
|
43
|
+
self.peek_limit,
|
|
44
|
+
)
|
|
45
|
+
self.mode = "normal"
|
|
46
|
+
self.suppressing = False
|
|
47
|
+
out, self.buf = self.buf, ""
|
|
48
|
+
return out
|
|
49
|
+
return ""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: projectdavid
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.32.0
|
|
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
|
|
@@ -27,7 +27,6 @@ Requires-Dist: validators<0.35.0,>=0.29.0
|
|
|
27
27
|
Requires-Dist: sentence-transformers<5.0,>=3.4.0
|
|
28
28
|
Requires-Dist: sseclient-py
|
|
29
29
|
Requires-Dist: requests
|
|
30
|
-
Requires-Dist: python-magic
|
|
31
30
|
Requires-Dist: python-docx
|
|
32
31
|
Requires-Dist: python-pptx
|
|
33
32
|
Provides-Extra: dev
|
|
@@ -9,13 +9,13 @@ projectdavid/clients/assistants_client.py,sha256=SsIGa5wPr7ga9WX0ywam3djUF-uWFdk
|
|
|
9
9
|
projectdavid/clients/base_client.py,sha256=UWl6nr6sxD1_xC6iyptQDR1tnNdFCOrEx5cEUPCRqJE,3417
|
|
10
10
|
projectdavid/clients/base_vector_store.py,sha256=jXivmqAW1bgYcLgIeW-hPxOiWZbs2hCsLy4oWzSvpNI,2061
|
|
11
11
|
projectdavid/clients/event_handler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
-
projectdavid/clients/file_processor.py,sha256
|
|
12
|
+
projectdavid/clients/file_processor.py,sha256=t-Uw-kBP_VmlguMxO9PWY6ANuMAY0PstQDW37wLPF0Q,13980
|
|
13
13
|
projectdavid/clients/file_search.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
14
|
projectdavid/clients/files_client.py,sha256=XkIDzbQFGDrd88taf0Kouc_4YJOPIYEHiIyWYLKDofI,15581
|
|
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=sikHDDmx3NFhK9eoFL0PVlZ0CaFG-CIWfDmgFkH48Do,4594
|
|
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,11 +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=uTjB6e1JDs_nkSVtRuqretb-zYDbAAC3jN3gpzqVauo,1616
|
|
32
33
|
projectdavid/utils/monitor_launcher.py,sha256=3YAgJdeuaUvq3JGvpA4ymqFsAnk29nH5q93cwStP4hc,2836
|
|
34
|
+
projectdavid/utils/peek_gate.py,sha256=odQIZJYTygRPu5IfOBBgqqeSXiWfQ73-lRBYM2KbY4I,1630
|
|
33
35
|
projectdavid/utils/run_monitor.py,sha256=F_WkqIP-qnWH-4llIbileWWLfRj2Q1Cg-ni23SR1rec,3786
|
|
34
36
|
projectdavid/utils/vector_search_formatter.py,sha256=YTe3HPGec26qGY7uxY8_GS8lc4QaN6aNXMzkl29nZpI,1735
|
|
35
|
-
projectdavid-1.
|
|
36
|
-
projectdavid-1.
|
|
37
|
-
projectdavid-1.
|
|
38
|
-
projectdavid-1.
|
|
39
|
-
projectdavid-1.
|
|
37
|
+
projectdavid-1.32.0.dist-info/licenses/LICENSE,sha256=_8yjiEGttpS284BkfhXxfERqTRZW_tUaHiBB0GTJTMg,4563
|
|
38
|
+
projectdavid-1.32.0.dist-info/METADATA,sha256=F3ZVLx0ylA3omK6BEsxsTrbaroIwcE42FO0GUcT0EkU,10781
|
|
39
|
+
projectdavid-1.32.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
40
|
+
projectdavid-1.32.0.dist-info/top_level.txt,sha256=kil8GU4s7qYRfNnzGnFHhZnSNRSxgNG-J4HLgQMmMtw,13
|
|
41
|
+
projectdavid-1.32.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|