llm-join 0.2.2__tar.gz → 0.2.3__tar.gz
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.
- {llm_join-0.2.2 → llm_join-0.2.3}/.gitignore +1 -0
- {llm_join-0.2.2 → llm_join-0.2.3}/PKG-INFO +3 -3
- {llm_join-0.2.2 → llm_join-0.2.3}/pyproject.toml +3 -3
- llm_join-0.2.2/notebooks/test_llm_join.ipynb +0 -404
- {llm_join-0.2.2 → llm_join-0.2.3}/.github/workflows/ci.yml +0 -0
- {llm_join-0.2.2 → llm_join-0.2.3}/README.md +0 -0
- {llm_join-0.2.2 → llm_join-0.2.3}/llm_join/__init__.py +0 -0
- {llm_join-0.2.2 → llm_join-0.2.3}/llm_join/config.py +0 -0
- {llm_join-0.2.2 → llm_join-0.2.3}/llm_join/join.py +0 -0
- {llm_join-0.2.2 → llm_join-0.2.3}/llm_join/merger.py +0 -0
- {llm_join-0.2.2 → llm_join-0.2.3}/llm_join/prompts.py +0 -0
- {llm_join-0.2.2 → llm_join-0.2.3}/llm_join/retriever.py +0 -0
- {llm_join-0.2.2 → llm_join-0.2.3}/llm_join/scorer.py +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: llm-join
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Fuzzy join DataFrames using LLM scoring and embedding retrieval
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: entity-resolution,fuzzy-join,llm,nlp,pandas
|
|
7
7
|
Requires-Python: >=3.9
|
|
8
|
-
Requires-Dist: faiss-cpu>=1.
|
|
9
|
-
Requires-Dist: numpy
|
|
8
|
+
Requires-Dist: faiss-cpu>=1.8
|
|
9
|
+
Requires-Dist: numpy>=1.23
|
|
10
10
|
Requires-Dist: pandas>=1.5
|
|
11
11
|
Provides-Extra: sentence-transformers
|
|
12
12
|
Requires-Dist: sentence-transformers>=2.2; extra == 'sentence-transformers'
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "llm-join"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.3"
|
|
8
8
|
description = "Fuzzy join DataFrames using LLM scoring and embedding retrieval"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -12,8 +12,8 @@ license = { text = "MIT" }
|
|
|
12
12
|
keywords = ["fuzzy-join", "llm", "entity-resolution", "pandas", "nlp"]
|
|
13
13
|
dependencies = [
|
|
14
14
|
"pandas>=1.5",
|
|
15
|
-
"numpy>=1.23
|
|
16
|
-
"faiss-cpu>=1.
|
|
15
|
+
"numpy>=1.23",
|
|
16
|
+
"faiss-cpu>=1.8",
|
|
17
17
|
]
|
|
18
18
|
|
|
19
19
|
[project.optional-dependencies]
|
|
@@ -1,404 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"cells": [
|
|
3
|
-
{
|
|
4
|
-
"cell_type": "markdown",
|
|
5
|
-
"metadata": {},
|
|
6
|
-
"source": [
|
|
7
|
-
"# llm-join — Test Notebook\n",
|
|
8
|
-
"\n",
|
|
9
|
-
"No API key needed. Uses:\n",
|
|
10
|
-
"- **Mock LLM** — scores candidates using word overlap + character similarity heuristics\n",
|
|
11
|
-
"- **Mock embed_fn** — TF-IDF style character n-gram vectors (numpy only, no sklearn)\n",
|
|
12
|
-
"\n",
|
|
13
|
-
"Swap the mock functions for your real LLM/embed when ready."
|
|
14
|
-
]
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
"cell_type": "markdown",
|
|
18
|
-
"metadata": {},
|
|
19
|
-
"source": [
|
|
20
|
-
"## 1. Install"
|
|
21
|
-
]
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"cell_type": "code",
|
|
25
|
-
"execution_count": null,
|
|
26
|
-
"metadata": {},
|
|
27
|
-
"outputs": [],
|
|
28
|
-
"source": [
|
|
29
|
-
"# Install from local clone (run from repo root) or GitHub\n",
|
|
30
|
-
"# pip install -e ..\n",
|
|
31
|
-
"# pip install git+https://github.com/adityabalki/llm-join.git\n",
|
|
32
|
-
"\n",
|
|
33
|
-
"import sys, os\n",
|
|
34
|
-
"sys.path.insert(0, os.path.abspath(\"..\")) # works if notebook is in /notebooks/"
|
|
35
|
-
]
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
"cell_type": "markdown",
|
|
39
|
-
"metadata": {},
|
|
40
|
-
"source": [
|
|
41
|
-
"## 2. Mock LLM and embed_fn (no API key needed)"
|
|
42
|
-
]
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
"cell_type": "code",
|
|
46
|
-
"execution_count": null,
|
|
47
|
-
"metadata": {},
|
|
48
|
-
"outputs": [],
|
|
49
|
-
"source": [
|
|
50
|
-
"import json\n",
|
|
51
|
-
"import re\n",
|
|
52
|
-
"import numpy as np\n",
|
|
53
|
-
"\n",
|
|
54
|
-
"\n",
|
|
55
|
-
"# ── Mock embed_fn ────────────────────────────────────────────────────────────\n",
|
|
56
|
-
"# Character n-gram (trigram) bag-of-words vectors.\n",
|
|
57
|
-
"# Good enough for faiss to retrieve plausible candidates.\n",
|
|
58
|
-
"\n",
|
|
59
|
-
"def get_trigrams(text: str) -> set:\n",
|
|
60
|
-
" t = text.lower()\n",
|
|
61
|
-
" return {t[i:i+3] for i in range(len(t) - 2)}\n",
|
|
62
|
-
"\n",
|
|
63
|
-
"def mock_embed(texts: list[str]) -> np.ndarray:\n",
|
|
64
|
-
" \"\"\"Returns (n, 300) float32 array using hashed trigram features.\"\"\"\n",
|
|
65
|
-
" dim = 300\n",
|
|
66
|
-
" vecs = np.zeros((len(texts), dim), dtype=\"float32\")\n",
|
|
67
|
-
" for i, text in enumerate(texts):\n",
|
|
68
|
-
" for tg in get_trigrams(text):\n",
|
|
69
|
-
" idx = hash(tg) % dim\n",
|
|
70
|
-
" vecs[i, idx] += 1.0\n",
|
|
71
|
-
" norm = np.linalg.norm(vecs[i])\n",
|
|
72
|
-
" if norm > 0:\n",
|
|
73
|
-
" vecs[i] /= norm\n",
|
|
74
|
-
" return vecs\n",
|
|
75
|
-
"\n",
|
|
76
|
-
"\n",
|
|
77
|
-
"# ── Mock LLM ─────────────────────────────────────────────────────────────────\n",
|
|
78
|
-
"# Parses the prompt, scores each LEFT|RIGHT pair using:\n",
|
|
79
|
-
"# - word overlap (Jaccard on lowercase words)\n",
|
|
80
|
-
"# - character trigram overlap\n",
|
|
81
|
-
"# Returns valid JSON the scorer can parse.\n",
|
|
82
|
-
"\n",
|
|
83
|
-
"def word_jaccard(a: str, b: str) -> float:\n",
|
|
84
|
-
" wa = set(re.sub(r'[^a-z0-9 ]', '', a.lower()).split())\n",
|
|
85
|
-
" wb = set(re.sub(r'[^a-z0-9 ]', '', b.lower()).split())\n",
|
|
86
|
-
" if not wa or not wb:\n",
|
|
87
|
-
" return 0.0\n",
|
|
88
|
-
" return len(wa & wb) / len(wa | wb)\n",
|
|
89
|
-
"\n",
|
|
90
|
-
"def trigram_overlap(a: str, b: str) -> float:\n",
|
|
91
|
-
" ta, tb = get_trigrams(a), get_trigrams(b)\n",
|
|
92
|
-
" if not ta or not tb:\n",
|
|
93
|
-
" return 0.0\n",
|
|
94
|
-
" return len(ta & tb) / len(ta | tb)\n",
|
|
95
|
-
"\n",
|
|
96
|
-
"def mock_llm(prompt: str) -> str:\n",
|
|
97
|
-
" \"\"\"Parses llm-join prompt format and returns JSON scores.\"\"\"\n",
|
|
98
|
-
" left_val = \"\"\n",
|
|
99
|
-
" candidates = []\n",
|
|
100
|
-
"\n",
|
|
101
|
-
" for line in prompt.splitlines():\n",
|
|
102
|
-
" m = re.match(r'(\\d+)\\. LEFT: \"(.+?)\" \\| RIGHT: \"(.+?)\"', line)\n",
|
|
103
|
-
" if m:\n",
|
|
104
|
-
" if not left_val:\n",
|
|
105
|
-
" left_val = m.group(2)\n",
|
|
106
|
-
" candidates.append((int(m.group(1)), m.group(2), m.group(3)))\n",
|
|
107
|
-
"\n",
|
|
108
|
-
" results = []\n",
|
|
109
|
-
" for idx, left, right in candidates:\n",
|
|
110
|
-
" wj = word_jaccard(left, right)\n",
|
|
111
|
-
" tg = trigram_overlap(left, right)\n",
|
|
112
|
-
" score = round(0.5 * wj + 0.5 * tg, 3)\n",
|
|
113
|
-
" if score > 0.6:\n",
|
|
114
|
-
" reasoning = \"strong word and character overlap\"\n",
|
|
115
|
-
" elif score > 0.3:\n",
|
|
116
|
-
" reasoning = \"partial match\"\n",
|
|
117
|
-
" else:\n",
|
|
118
|
-
" reasoning = \"low similarity\"\n",
|
|
119
|
-
" results.append({\"index\": idx, \"score\": score, \"reasoning\": reasoning})\n",
|
|
120
|
-
"\n",
|
|
121
|
-
" return json.dumps(results)\n",
|
|
122
|
-
"\n",
|
|
123
|
-
"\n",
|
|
124
|
-
"# Quick sanity check\n",
|
|
125
|
-
"print(mock_embed([\"Goldman Sachs\", \"Amazon\"]).shape) # (2, 300)\n",
|
|
126
|
-
"print(mock_llm('0. LEFT: \"aspirin\" | RIGHT: \"Bayer Aspirin\"'))"
|
|
127
|
-
]
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
"cell_type": "markdown",
|
|
131
|
-
"metadata": {},
|
|
132
|
-
"source": [
|
|
133
|
-
"## 3. Test Data"
|
|
134
|
-
]
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
"cell_type": "code",
|
|
138
|
-
"execution_count": null,
|
|
139
|
-
"metadata": {},
|
|
140
|
-
"outputs": [],
|
|
141
|
-
"source": [
|
|
142
|
-
"import pandas as pd\n",
|
|
143
|
-
"\n",
|
|
144
|
-
"# Left: your internal vendor list\n",
|
|
145
|
-
"df1 = pd.DataFrame({\n",
|
|
146
|
-
" \"vendor\": [\"Goldman Sachs & Co.\", \"Amazon Web Services\", \"Microsoft Corp\",\n",
|
|
147
|
-
" \"Apple Inc\", \"JP Morgan\", \"Unmatched Vendor Ltd\"],\n",
|
|
148
|
-
" \"spend\": [1_200_000, 890_000, 340_000, 500_000, 220_000, 99_000],\n",
|
|
149
|
-
"})\n",
|
|
150
|
-
"\n",
|
|
151
|
-
"# Right: supplier master (different naming conventions)\n",
|
|
152
|
-
"df2 = pd.DataFrame({\n",
|
|
153
|
-
" \"supplier_name\": [\"The Goldman Sachs Group Inc\", \"Amazon.com Inc.\",\n",
|
|
154
|
-
" \"Microsoft Corporation\", \"Apple Incorporated\",\n",
|
|
155
|
-
" \"JPMorgan Chase & Co.\", \"Extra Supplier Not In Left\"],\n",
|
|
156
|
-
" \"category\": [\"Finance\", \"Cloud\", \"Software\", \"Hardware\", \"Finance\", \"Other\"],\n",
|
|
157
|
-
"})\n",
|
|
158
|
-
"\n",
|
|
159
|
-
"print(\"df1:\"); display(df1)\n",
|
|
160
|
-
"print(\"df2:\"); display(df2)"
|
|
161
|
-
]
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
"cell_type": "markdown",
|
|
165
|
-
"metadata": {},
|
|
166
|
-
"source": [
|
|
167
|
-
"## 4. Basic Inner Join"
|
|
168
|
-
]
|
|
169
|
-
},
|
|
170
|
-
{
|
|
171
|
-
"cell_type": "code",
|
|
172
|
-
"execution_count": null,
|
|
173
|
-
"metadata": {},
|
|
174
|
-
"outputs": [],
|
|
175
|
-
"source": [
|
|
176
|
-
"from llm_join import fuzzy_join\n",
|
|
177
|
-
"\n",
|
|
178
|
-
"result = fuzzy_join(\n",
|
|
179
|
-
" df1, df2,\n",
|
|
180
|
-
" left_on=\"vendor\",\n",
|
|
181
|
-
" right_on=\"supplier_name\",\n",
|
|
182
|
-
" llm=mock_llm,\n",
|
|
183
|
-
" embed_fn=mock_embed,\n",
|
|
184
|
-
" context=\"company vendor names — match legal entity variants and abbreviations\",\n",
|
|
185
|
-
" how=\"inner\",\n",
|
|
186
|
-
" threshold=0.3, # lower threshold for mock (real LLM scores higher)\n",
|
|
187
|
-
")\n",
|
|
188
|
-
"\n",
|
|
189
|
-
"display(result)"
|
|
190
|
-
]
|
|
191
|
-
},
|
|
192
|
-
{
|
|
193
|
-
"cell_type": "markdown",
|
|
194
|
-
"metadata": {},
|
|
195
|
-
"source": [
|
|
196
|
-
"## 5. Left Join — See What Didn't Match"
|
|
197
|
-
]
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
"cell_type": "code",
|
|
201
|
-
"execution_count": null,
|
|
202
|
-
"metadata": {},
|
|
203
|
-
"outputs": [],
|
|
204
|
-
"source": [
|
|
205
|
-
"result_left = fuzzy_join(\n",
|
|
206
|
-
" df1, df2,\n",
|
|
207
|
-
" left_on=\"vendor\",\n",
|
|
208
|
-
" right_on=\"supplier_name\",\n",
|
|
209
|
-
" llm=mock_llm,\n",
|
|
210
|
-
" embed_fn=mock_embed,\n",
|
|
211
|
-
" how=\"left\",\n",
|
|
212
|
-
" threshold=0.3,\n",
|
|
213
|
-
")\n",
|
|
214
|
-
"\n",
|
|
215
|
-
"display(result_left)\n",
|
|
216
|
-
"\n",
|
|
217
|
-
"unmatched = result_left[result_left[\"supplier_name\"].isna()]\n",
|
|
218
|
-
"print(f\"\\nUnmatched left rows ({len(unmatched)}):\")\n",
|
|
219
|
-
"display(unmatched)"
|
|
220
|
-
]
|
|
221
|
-
},
|
|
222
|
-
{
|
|
223
|
-
"cell_type": "markdown",
|
|
224
|
-
"metadata": {},
|
|
225
|
-
"source": [
|
|
226
|
-
"## 6. Return Reasoning — See Why Each Match Was Made"
|
|
227
|
-
]
|
|
228
|
-
},
|
|
229
|
-
{
|
|
230
|
-
"cell_type": "code",
|
|
231
|
-
"execution_count": null,
|
|
232
|
-
"metadata": {},
|
|
233
|
-
"outputs": [],
|
|
234
|
-
"source": [
|
|
235
|
-
"result_reasoned = fuzzy_join(\n",
|
|
236
|
-
" df1, df2,\n",
|
|
237
|
-
" left_on=\"vendor\",\n",
|
|
238
|
-
" right_on=\"supplier_name\",\n",
|
|
239
|
-
" llm=mock_llm,\n",
|
|
240
|
-
" embed_fn=mock_embed,\n",
|
|
241
|
-
" how=\"left\",\n",
|
|
242
|
-
" threshold=0.3,\n",
|
|
243
|
-
" return_reasoning=True,\n",
|
|
244
|
-
")\n",
|
|
245
|
-
"\n",
|
|
246
|
-
"display(result_reasoned[[\"vendor\", \"supplier_name\", \"_llm_score\", \"_llm_reasoning\", \"_embed_rank\", \"_match_method\"]])"
|
|
247
|
-
]
|
|
248
|
-
},
|
|
249
|
-
{
|
|
250
|
-
"cell_type": "markdown",
|
|
251
|
-
"metadata": {},
|
|
252
|
-
"source": [
|
|
253
|
-
"## 7. Outer Join — Full Reconciliation (both gaps)"
|
|
254
|
-
]
|
|
255
|
-
},
|
|
256
|
-
{
|
|
257
|
-
"cell_type": "code",
|
|
258
|
-
"execution_count": null,
|
|
259
|
-
"metadata": {},
|
|
260
|
-
"outputs": [],
|
|
261
|
-
"source": [
|
|
262
|
-
"result_outer = fuzzy_join(\n",
|
|
263
|
-
" df1, df2,\n",
|
|
264
|
-
" left_on=\"vendor\",\n",
|
|
265
|
-
" right_on=\"supplier_name\",\n",
|
|
266
|
-
" llm=mock_llm,\n",
|
|
267
|
-
" embed_fn=mock_embed,\n",
|
|
268
|
-
" how=\"outer\",\n",
|
|
269
|
-
" threshold=0.3,\n",
|
|
270
|
-
")\n",
|
|
271
|
-
"\n",
|
|
272
|
-
"display(result_outer)\n",
|
|
273
|
-
"\n",
|
|
274
|
-
"# Rows in supplier master with no match in your vendor list\n",
|
|
275
|
-
"unmatched_right = result_outer[result_outer[\"vendor\"].isna()]\n",
|
|
276
|
-
"print(f\"\\nRight rows with no left match ({len(unmatched_right)}):\")\n",
|
|
277
|
-
"display(unmatched_right)"
|
|
278
|
-
]
|
|
279
|
-
},
|
|
280
|
-
{
|
|
281
|
-
"cell_type": "markdown",
|
|
282
|
-
"metadata": {},
|
|
283
|
-
"source": [
|
|
284
|
-
"## 8. Cost Controls"
|
|
285
|
-
]
|
|
286
|
-
},
|
|
287
|
-
{
|
|
288
|
-
"cell_type": "code",
|
|
289
|
-
"execution_count": null,
|
|
290
|
-
"metadata": {},
|
|
291
|
-
"outputs": [],
|
|
292
|
-
"source": [
|
|
293
|
-
"import warnings\n",
|
|
294
|
-
"\n",
|
|
295
|
-
"result_capped = fuzzy_join(\n",
|
|
296
|
-
" df1, df2,\n",
|
|
297
|
-
" left_on=\"vendor\",\n",
|
|
298
|
-
" right_on=\"supplier_name\",\n",
|
|
299
|
-
" llm=mock_llm,\n",
|
|
300
|
-
" embed_fn=mock_embed,\n",
|
|
301
|
-
" top_k=3, # fewer candidates per row\n",
|
|
302
|
-
" threshold=0.3,\n",
|
|
303
|
-
" max_llm_calls=3, # cap at 3 LLM calls (will warn and return partial)\n",
|
|
304
|
-
" how=\"left\",\n",
|
|
305
|
-
")\n",
|
|
306
|
-
"\n",
|
|
307
|
-
"display(result_capped)"
|
|
308
|
-
]
|
|
309
|
-
},
|
|
310
|
-
{
|
|
311
|
-
"cell_type": "markdown",
|
|
312
|
-
"metadata": {},
|
|
313
|
-
"source": [
|
|
314
|
-
"## 9. Chained Joins"
|
|
315
|
-
]
|
|
316
|
-
},
|
|
317
|
-
{
|
|
318
|
-
"cell_type": "code",
|
|
319
|
-
"execution_count": null,
|
|
320
|
-
"metadata": {},
|
|
321
|
-
"outputs": [],
|
|
322
|
-
"source": [
|
|
323
|
-
"# df1: transactions with vendor + product\n",
|
|
324
|
-
"transactions = pd.DataFrame({\n",
|
|
325
|
-
" \"vendor\": [\"Goldman Sachs & Co.\", \"Microsoft Corp\"],\n",
|
|
326
|
-
" \"product\": [\"equity trading platform\", \"office suite software\"],\n",
|
|
327
|
-
" \"amount\": [50_000, 12_000],\n",
|
|
328
|
-
"})\n",
|
|
329
|
-
"\n",
|
|
330
|
-
"product_catalog = pd.DataFrame({\n",
|
|
331
|
-
" \"catalog_item\": [\"Equity Trading System\", \"Microsoft Office 365\", \"Azure Cloud\"],\n",
|
|
332
|
-
" \"sku\": [\"EQ-001\", \"MS-OFF-365\", \"AZ-001\"],\n",
|
|
333
|
-
"})\n",
|
|
334
|
-
"\n",
|
|
335
|
-
"# Step 1: match vendors\n",
|
|
336
|
-
"step1 = fuzzy_join(\n",
|
|
337
|
-
" transactions, df2,\n",
|
|
338
|
-
" left_on=\"vendor\", right_on=\"supplier_name\",\n",
|
|
339
|
-
" llm=mock_llm, embed_fn=mock_embed,\n",
|
|
340
|
-
" how=\"left\", threshold=0.3,\n",
|
|
341
|
-
" return_reasoning=True,\n",
|
|
342
|
-
")\n",
|
|
343
|
-
"step1 = step1.rename(columns={\n",
|
|
344
|
-
" \"_llm_score\": \"_vendor_score\",\n",
|
|
345
|
-
" \"_llm_reasoning\": \"_vendor_reasoning\",\n",
|
|
346
|
-
" \"_match_method\": \"_vendor_method\",\n",
|
|
347
|
-
" \"_embed_rank\": \"_vendor_embed_rank\",\n",
|
|
348
|
-
"})\n",
|
|
349
|
-
"\n",
|
|
350
|
-
"# Step 2: match products\n",
|
|
351
|
-
"result_chained = fuzzy_join(\n",
|
|
352
|
-
" step1, product_catalog,\n",
|
|
353
|
-
" left_on=\"product\", right_on=\"catalog_item\",\n",
|
|
354
|
-
" llm=mock_llm, embed_fn=mock_embed,\n",
|
|
355
|
-
" how=\"left\", threshold=0.1,\n",
|
|
356
|
-
" return_reasoning=True,\n",
|
|
357
|
-
")\n",
|
|
358
|
-
"\n",
|
|
359
|
-
"display(result_chained)"
|
|
360
|
-
]
|
|
361
|
-
},
|
|
362
|
-
{
|
|
363
|
-
"cell_type": "markdown",
|
|
364
|
-
"metadata": {},
|
|
365
|
-
"source": [
|
|
366
|
-
"## 10. Swap to Real LLM / Embed\n",
|
|
367
|
-
"\n",
|
|
368
|
-
"When you have API access, replace the mock functions:\n",
|
|
369
|
-
"\n",
|
|
370
|
-
"```python\n",
|
|
371
|
-
"import openai, numpy as np\n",
|
|
372
|
-
"client = openai.OpenAI(api_key=\"...\") # or base_url=\"https://your-enterprise-endpoint\"\n",
|
|
373
|
-
"\n",
|
|
374
|
-
"def my_llm(prompt):\n",
|
|
375
|
-
" return client.chat.completions.create(\n",
|
|
376
|
-
" model=\"gpt-4o-mini\",\n",
|
|
377
|
-
" messages=[{\"role\": \"user\", \"content\": prompt}]\n",
|
|
378
|
-
" ).choices[0].message.content\n",
|
|
379
|
-
"\n",
|
|
380
|
-
"def my_embed(texts):\n",
|
|
381
|
-
" response = client.embeddings.create(model=\"text-embedding-3-small\", input=texts)\n",
|
|
382
|
-
" return np.array([d.embedding for d in response.data], dtype=\"float32\")\n",
|
|
383
|
-
"\n",
|
|
384
|
-
"# Then use threshold=0.7 (default) instead of 0.3\n",
|
|
385
|
-
"result = fuzzy_join(df1, df2, left_on=\"vendor\", right_on=\"supplier_name\",\n",
|
|
386
|
-
" llm=my_llm, embed_fn=my_embed)\n",
|
|
387
|
-
"```"
|
|
388
|
-
]
|
|
389
|
-
}
|
|
390
|
-
],
|
|
391
|
-
"metadata": {
|
|
392
|
-
"kernelspec": {
|
|
393
|
-
"display_name": "Python 3",
|
|
394
|
-
"language": "python",
|
|
395
|
-
"name": "python3"
|
|
396
|
-
},
|
|
397
|
-
"language_info": {
|
|
398
|
-
"name": "python",
|
|
399
|
-
"version": "3.12.0"
|
|
400
|
-
}
|
|
401
|
-
},
|
|
402
|
-
"nbformat": 4,
|
|
403
|
-
"nbformat_minor": 4
|
|
404
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|