Spaces:
Running
Running
Merge remote-tracking branch 'origin/main' into darabos-folders
Browse files- examples/LynxScribe Data Cleaning.lynxkite.json +536 -0
- examples/LynxScribe Image Search.lynxkite.json +51 -57
- examples/uploads/task_solver_examples.xlsx +0 -0
- lynxkite-app/src/lynxkite_app/crdt.py +1 -0
- lynxkite-core/src/lynxkite/core/executors/simple.py +64 -0
- lynxkite-core/src/lynxkite/core/ops.py +1 -0
- lynxkite-core/src/lynxkite/core/workspace.py +19 -0
- lynxkite-lynxscribe/src/lynxkite_lynxscribe/lynxscribe_ops.py +138 -2
- lynxkite-pillow-example/src/lynxkite_pillow_example/__init__.py +2 -2
examples/LynxScribe Data Cleaning.lynxkite.json
ADDED
@@ -0,0 +1,536 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"edges": [
|
3 |
+
{
|
4 |
+
"id": "LynxScribe Message 3 LynxScribe Task Solver 1",
|
5 |
+
"source": "LynxScribe Message 3",
|
6 |
+
"sourceHandle": "output",
|
7 |
+
"target": "LynxScribe Task Solver 1",
|
8 |
+
"targetHandle": "system_prompt"
|
9 |
+
},
|
10 |
+
{
|
11 |
+
"id": "LynxScribe Message 1 LynxScribe Task Solver 1",
|
12 |
+
"source": "LynxScribe Message 1",
|
13 |
+
"sourceHandle": "output",
|
14 |
+
"target": "LynxScribe Task Solver 1",
|
15 |
+
"targetHandle": "instruction_prompt"
|
16 |
+
},
|
17 |
+
{
|
18 |
+
"id": "Read Excel 1 LynxScribe Task Solver 1",
|
19 |
+
"source": "Read Excel 1",
|
20 |
+
"sourceHandle": "output",
|
21 |
+
"target": "LynxScribe Task Solver 1",
|
22 |
+
"targetHandle": "dataframe"
|
23 |
+
},
|
24 |
+
{
|
25 |
+
"id": "LynxScribe Task Solver 1 View DataFrame 1",
|
26 |
+
"source": "LynxScribe Task Solver 1",
|
27 |
+
"sourceHandle": "output",
|
28 |
+
"target": "View DataFrame 1",
|
29 |
+
"targetHandle": "input"
|
30 |
+
},
|
31 |
+
{
|
32 |
+
"id": "Read Excel 1 View DataFrame 2",
|
33 |
+
"source": "Read Excel 1",
|
34 |
+
"sourceHandle": "output",
|
35 |
+
"target": "View DataFrame 2",
|
36 |
+
"targetHandle": "input"
|
37 |
+
}
|
38 |
+
],
|
39 |
+
"env": "LynxScribe",
|
40 |
+
"nodes": [
|
41 |
+
{
|
42 |
+
"data": {
|
43 |
+
"__execution_delay": 0.0,
|
44 |
+
"collapsed": null,
|
45 |
+
"display": null,
|
46 |
+
"error": null,
|
47 |
+
"input_metadata": null,
|
48 |
+
"meta": {
|
49 |
+
"inputs": {},
|
50 |
+
"name": "LynxScribe Message",
|
51 |
+
"outputs": {
|
52 |
+
"output": {
|
53 |
+
"name": "output",
|
54 |
+
"position": "top",
|
55 |
+
"type": {
|
56 |
+
"type": "None"
|
57 |
+
}
|
58 |
+
}
|
59 |
+
},
|
60 |
+
"params": {
|
61 |
+
"prompt_content": {
|
62 |
+
"default": null,
|
63 |
+
"name": "prompt_content",
|
64 |
+
"type": {
|
65 |
+
"format": "textarea"
|
66 |
+
}
|
67 |
+
},
|
68 |
+
"prompt_role": {
|
69 |
+
"default": null,
|
70 |
+
"name": "prompt_role",
|
71 |
+
"type": {
|
72 |
+
"enum": [
|
73 |
+
"SYSTEM",
|
74 |
+
"USER"
|
75 |
+
]
|
76 |
+
}
|
77 |
+
}
|
78 |
+
},
|
79 |
+
"position": {
|
80 |
+
"x": 653.0,
|
81 |
+
"y": 954.0
|
82 |
+
},
|
83 |
+
"type": "basic"
|
84 |
+
},
|
85 |
+
"params": {
|
86 |
+
"prompt_content": "You are an AI assistant designed to clean and extract structured address information from raw text.\nYour goal is to identify and extract relevant address components while ignoring any unrelated information.\nThe output must be formatted as a structured dictionary.\n\nYour task is to parse an address from raw text and return a dictionary with the following keys:\n - zip_code: The postal or ZIP code.\n - country: The country name.\n - state_or_county: The state, province, or county (if applicable).\n - city: The city or town name.\n - district: The district or borough name (if mentioned).\n - street_type: The type of public space (e.g., street, avenue, boulevard, square).\n - street_name: The name of the public space (e.g., Main, Baker, Champs-\u00c9lys\u00e9es).\n - house_number: The house or building number.\n - floor: The floor number (if mentioned).\n - flat_number: The apartment or unit number (if mentioned).\n - additional_info: Any other useful details, such as building names, or known landmarks.\n\nIf any information is missing from the input, leave the corresponding key as an empty string.\n\nYou must return only a python dictionary with the following keys:\n`zip_code`, `country`, `state_or_county`, `city`, `district`, `street_name`, \n`house_number`, `floor`, `flat_number`, `additional_info`.\n\nDo not include any extra text, comments, or explanations\u2014only return the dictionary.",
|
87 |
+
"prompt_role": "SYSTEM"
|
88 |
+
},
|
89 |
+
"status": "done",
|
90 |
+
"title": "LynxScribe Message"
|
91 |
+
},
|
92 |
+
"dragHandle": ".bg-primary",
|
93 |
+
"height": 354.0,
|
94 |
+
"id": "LynxScribe Message 3",
|
95 |
+
"position": {
|
96 |
+
"x": 36.0,
|
97 |
+
"y": 569.0
|
98 |
+
},
|
99 |
+
"type": "basic",
|
100 |
+
"width": 740.0
|
101 |
+
},
|
102 |
+
{
|
103 |
+
"data": {
|
104 |
+
"__execution_delay": 0.0,
|
105 |
+
"collapsed": null,
|
106 |
+
"display": null,
|
107 |
+
"error": null,
|
108 |
+
"input_metadata": null,
|
109 |
+
"meta": {
|
110 |
+
"inputs": {},
|
111 |
+
"name": "LynxScribe Message",
|
112 |
+
"outputs": {
|
113 |
+
"output": {
|
114 |
+
"name": "output",
|
115 |
+
"position": "top",
|
116 |
+
"type": {
|
117 |
+
"type": "None"
|
118 |
+
}
|
119 |
+
}
|
120 |
+
},
|
121 |
+
"params": {
|
122 |
+
"prompt_content": {
|
123 |
+
"default": null,
|
124 |
+
"name": "prompt_content",
|
125 |
+
"type": {
|
126 |
+
"format": "textarea"
|
127 |
+
}
|
128 |
+
},
|
129 |
+
"prompt_role": {
|
130 |
+
"default": null,
|
131 |
+
"name": "prompt_role",
|
132 |
+
"type": {
|
133 |
+
"enum": [
|
134 |
+
"SYSTEM",
|
135 |
+
"USER"
|
136 |
+
]
|
137 |
+
}
|
138 |
+
}
|
139 |
+
},
|
140 |
+
"position": {
|
141 |
+
"x": 1498.0,
|
142 |
+
"y": 660.0
|
143 |
+
},
|
144 |
+
"type": "basic"
|
145 |
+
},
|
146 |
+
"params": {
|
147 |
+
"prompt_content": "Extract structured address information from the following text: {message_parts}",
|
148 |
+
"prompt_role": "USER"
|
149 |
+
},
|
150 |
+
"status": "done",
|
151 |
+
"title": "LynxScribe Message"
|
152 |
+
},
|
153 |
+
"dragHandle": ".bg-primary",
|
154 |
+
"height": 347.0,
|
155 |
+
"id": "LynxScribe Message 1",
|
156 |
+
"position": {
|
157 |
+
"x": 817.0,
|
158 |
+
"y": 566.0
|
159 |
+
},
|
160 |
+
"type": "basic",
|
161 |
+
"width": 498.0
|
162 |
+
},
|
163 |
+
{
|
164 |
+
"data": {
|
165 |
+
"__execution_delay": 0.0,
|
166 |
+
"collapsed": null,
|
167 |
+
"display": null,
|
168 |
+
"error": null,
|
169 |
+
"input_metadata": null,
|
170 |
+
"meta": {
|
171 |
+
"inputs": {},
|
172 |
+
"name": "Read Excel",
|
173 |
+
"outputs": {
|
174 |
+
"output": {
|
175 |
+
"name": "output",
|
176 |
+
"position": "right",
|
177 |
+
"type": {
|
178 |
+
"type": "None"
|
179 |
+
}
|
180 |
+
}
|
181 |
+
},
|
182 |
+
"params": {
|
183 |
+
"columns": {
|
184 |
+
"default": "",
|
185 |
+
"name": "columns",
|
186 |
+
"type": {
|
187 |
+
"type": "<class 'str'>"
|
188 |
+
}
|
189 |
+
},
|
190 |
+
"file_path": {
|
191 |
+
"default": null,
|
192 |
+
"name": "file_path",
|
193 |
+
"type": {
|
194 |
+
"type": "<class 'str'>"
|
195 |
+
}
|
196 |
+
},
|
197 |
+
"sheet_name": {
|
198 |
+
"default": "Sheet1",
|
199 |
+
"name": "sheet_name",
|
200 |
+
"type": {
|
201 |
+
"type": "<class 'str'>"
|
202 |
+
}
|
203 |
+
}
|
204 |
+
},
|
205 |
+
"position": {
|
206 |
+
"x": 236.0,
|
207 |
+
"y": 150.0
|
208 |
+
},
|
209 |
+
"type": "basic"
|
210 |
+
},
|
211 |
+
"params": {
|
212 |
+
"columns": "",
|
213 |
+
"file_path": "uploads/task_solver_examples.xlsx",
|
214 |
+
"sheet_name": "address_example"
|
215 |
+
},
|
216 |
+
"status": "done",
|
217 |
+
"title": "Read Excel"
|
218 |
+
},
|
219 |
+
"dragHandle": ".bg-primary",
|
220 |
+
"height": 284.0,
|
221 |
+
"id": "Read Excel 1",
|
222 |
+
"position": {
|
223 |
+
"x": 41.0,
|
224 |
+
"y": 168.0
|
225 |
+
},
|
226 |
+
"type": "basic",
|
227 |
+
"width": 332.0
|
228 |
+
},
|
229 |
+
{
|
230 |
+
"data": {
|
231 |
+
"__execution_delay": 0.0,
|
232 |
+
"collapsed": null,
|
233 |
+
"display": null,
|
234 |
+
"error": null,
|
235 |
+
"input_metadata": null,
|
236 |
+
"meta": {
|
237 |
+
"inputs": {
|
238 |
+
"dataframe": {
|
239 |
+
"name": "dataframe",
|
240 |
+
"position": "left",
|
241 |
+
"type": {
|
242 |
+
"type": "<class 'inspect._empty'>"
|
243 |
+
}
|
244 |
+
},
|
245 |
+
"instruction_prompt": {
|
246 |
+
"name": "instruction_prompt",
|
247 |
+
"position": "bottom",
|
248 |
+
"type": {
|
249 |
+
"type": "<class 'inspect._empty'>"
|
250 |
+
}
|
251 |
+
},
|
252 |
+
"system_prompt": {
|
253 |
+
"name": "system_prompt",
|
254 |
+
"position": "bottom",
|
255 |
+
"type": {
|
256 |
+
"type": "<class 'inspect._empty'>"
|
257 |
+
}
|
258 |
+
}
|
259 |
+
},
|
260 |
+
"name": "LynxScribe Task Solver",
|
261 |
+
"outputs": {
|
262 |
+
"output": {
|
263 |
+
"name": "output",
|
264 |
+
"position": "right",
|
265 |
+
"type": {
|
266 |
+
"type": "None"
|
267 |
+
}
|
268 |
+
}
|
269 |
+
},
|
270 |
+
"params": {
|
271 |
+
"llm_interface": {
|
272 |
+
"default": "openai",
|
273 |
+
"name": "llm_interface",
|
274 |
+
"type": {
|
275 |
+
"type": "<class 'str'>"
|
276 |
+
}
|
277 |
+
},
|
278 |
+
"llm_model_name": {
|
279 |
+
"default": "gpt-4o",
|
280 |
+
"name": "llm_model_name",
|
281 |
+
"type": {
|
282 |
+
"type": "<class 'str'>"
|
283 |
+
}
|
284 |
+
},
|
285 |
+
"new_column_names": {
|
286 |
+
"default": "processed_field",
|
287 |
+
"name": "new_column_names",
|
288 |
+
"type": {
|
289 |
+
"type": "<class 'str'>"
|
290 |
+
}
|
291 |
+
}
|
292 |
+
},
|
293 |
+
"position": {
|
294 |
+
"x": 1511.0,
|
295 |
+
"y": 220.0
|
296 |
+
},
|
297 |
+
"type": "basic"
|
298 |
+
},
|
299 |
+
"params": {
|
300 |
+
"llm_interface": "openai",
|
301 |
+
"llm_model_name": "gpt-4o-mini",
|
302 |
+
"new_column_names": "zip_code, country, state_or_county, city, district, street_name, house_number, floor, flat_number, additional_info"
|
303 |
+
},
|
304 |
+
"status": "done",
|
305 |
+
"title": "LynxScribe Task Solver"
|
306 |
+
},
|
307 |
+
"dragHandle": ".bg-primary",
|
308 |
+
"height": 282.0,
|
309 |
+
"id": "LynxScribe Task Solver 1",
|
310 |
+
"position": {
|
311 |
+
"x": 626.0,
|
312 |
+
"y": 170.0
|
313 |
+
},
|
314 |
+
"type": "basic",
|
315 |
+
"width": 272.0
|
316 |
+
},
|
317 |
+
{
|
318 |
+
"data": {
|
319 |
+
"display": {
|
320 |
+
"dataframes": {
|
321 |
+
"df": {
|
322 |
+
"columns": [
|
323 |
+
"message_parts",
|
324 |
+
"zip_code",
|
325 |
+
"country",
|
326 |
+
"state_or_county",
|
327 |
+
"city",
|
328 |
+
"district",
|
329 |
+
"street_name",
|
330 |
+
"house_number",
|
331 |
+
"floor",
|
332 |
+
"flat_number",
|
333 |
+
"additional_info"
|
334 |
+
],
|
335 |
+
"data": [
|
336 |
+
[
|
337 |
+
"John's old apartment: 742 Evergreen Terrace, Springfield, IL 62704, USA. Call me at +1-555-1234 or email [email protected].",
|
338 |
+
"62704",
|
339 |
+
"USA",
|
340 |
+
"IL",
|
341 |
+
"Springfield",
|
342 |
+
"",
|
343 |
+
"Evergreen Terrace",
|
344 |
+
"742",
|
345 |
+
"",
|
346 |
+
"",
|
347 |
+
"John's old apartment"
|
348 |
+
],
|
349 |
+
[
|
350 |
+
"Visit our office at 56B Baker Street, Marylebone, London W1U 8ED, UK. (Nearest Tube: Baker Street). Contact: [email protected].",
|
351 |
+
"W1U 8ED",
|
352 |
+
"UK",
|
353 |
+
"",
|
354 |
+
"London",
|
355 |
+
"Marylebone",
|
356 |
+
"Baker",
|
357 |
+
"56B",
|
358 |
+
"",
|
359 |
+
"",
|
360 |
+
""
|
361 |
+
],
|
362 |
+
[
|
363 |
+
"New residence: 300, 5th Avenue, New York, NY 10001, USA. Floor 12, Apt 1204. My new phone is (212) 555-6789.",
|
364 |
+
"10001",
|
365 |
+
"USA",
|
366 |
+
"NY",
|
367 |
+
"New York",
|
368 |
+
"",
|
369 |
+
"5th Avenue",
|
370 |
+
"300",
|
371 |
+
"12",
|
372 |
+
"1204",
|
373 |
+
""
|
374 |
+
],
|
375 |
+
[
|
376 |
+
"We just moved to 23 rue de la Paix, 75002 Paris, France. Floor 3, Flat 5. Send mail to my old address instead.",
|
377 |
+
"75002",
|
378 |
+
"France",
|
379 |
+
"",
|
380 |
+
"Paris",
|
381 |
+
"",
|
382 |
+
"rue de la Paix",
|
383 |
+
"23",
|
384 |
+
"3",
|
385 |
+
"5",
|
386 |
+
""
|
387 |
+
],
|
388 |
+
[
|
389 |
+
"Warehouse location: 1024 Industrial Blvd, Houston, TX 77002, USA. Not open on weekends. Customer support: [email protected].",
|
390 |
+
"77002",
|
391 |
+
"USA",
|
392 |
+
"TX",
|
393 |
+
"Houston",
|
394 |
+
"",
|
395 |
+
"Industrial Blvd",
|
396 |
+
"1024",
|
397 |
+
"",
|
398 |
+
"",
|
399 |
+
"Warehouse location"
|
400 |
+
],
|
401 |
+
[
|
402 |
+
"My grandma lives at 2F, 15-3 Shinjuku, Tokyo 160-0022, Japan. Don't use my old phone number anymore!",
|
403 |
+
"160-0022",
|
404 |
+
"Japan",
|
405 |
+
"",
|
406 |
+
"Tokyo",
|
407 |
+
"Shinjuku",
|
408 |
+
"Shinjuku",
|
409 |
+
"15-3",
|
410 |
+
"2F",
|
411 |
+
"",
|
412 |
+
""
|
413 |
+
],
|
414 |
+
[
|
415 |
+
"Delivery address: Apt 9, 88 Queen's Road Central, Central, Hong Kong. Landmark: Opposite IFC Mall.",
|
416 |
+
"",
|
417 |
+
"Hong Kong",
|
418 |
+
"",
|
419 |
+
"Central",
|
420 |
+
"",
|
421 |
+
"Queen's Road Central",
|
422 |
+
"88",
|
423 |
+
"",
|
424 |
+
"9",
|
425 |
+
"Opposite IFC Mall"
|
426 |
+
]
|
427 |
+
]
|
428 |
+
}
|
429 |
+
}
|
430 |
+
},
|
431 |
+
"error": null,
|
432 |
+
"input_metadata": null,
|
433 |
+
"meta": {
|
434 |
+
"inputs": {
|
435 |
+
"input": {
|
436 |
+
"name": "input",
|
437 |
+
"position": "bottom",
|
438 |
+
"type": {
|
439 |
+
"type": "<class 'inspect._empty'>"
|
440 |
+
}
|
441 |
+
}
|
442 |
+
},
|
443 |
+
"name": "View DataFrame",
|
444 |
+
"outputs": {},
|
445 |
+
"params": {},
|
446 |
+
"position": {
|
447 |
+
"x": 1719.0,
|
448 |
+
"y": 332.0
|
449 |
+
},
|
450 |
+
"type": "table_view"
|
451 |
+
},
|
452 |
+
"params": {},
|
453 |
+
"status": "done",
|
454 |
+
"title": "View DataFrame"
|
455 |
+
},
|
456 |
+
"dragHandle": ".bg-primary",
|
457 |
+
"height": 316.0,
|
458 |
+
"id": "View DataFrame 1",
|
459 |
+
"position": {
|
460 |
+
"x": 1139.0,
|
461 |
+
"y": 91.0
|
462 |
+
},
|
463 |
+
"type": "table_view",
|
464 |
+
"width": 1118.0
|
465 |
+
},
|
466 |
+
{
|
467 |
+
"data": {
|
468 |
+
"display": {
|
469 |
+
"dataframes": {
|
470 |
+
"df": {
|
471 |
+
"columns": [
|
472 |
+
"message_parts"
|
473 |
+
],
|
474 |
+
"data": [
|
475 |
+
[
|
476 |
+
"John's old apartment: 742 Evergreen Terrace, Springfield, IL 62704, USA. Call me at +1-555-1234 or email [email protected]."
|
477 |
+
],
|
478 |
+
[
|
479 |
+
"Visit our office at 56B Baker Street, Marylebone, London W1U 8ED, UK. (Nearest Tube: Baker Street). Contact: [email protected]."
|
480 |
+
],
|
481 |
+
[
|
482 |
+
"New residence: 300, 5th Avenue, New York, NY 10001, USA. Floor 12, Apt 1204. My new phone is (212) 555-6789."
|
483 |
+
],
|
484 |
+
[
|
485 |
+
"We just moved to 23 rue de la Paix, 75002 Paris, France. Floor 3, Flat 5. Send mail to my old address instead."
|
486 |
+
],
|
487 |
+
[
|
488 |
+
"Warehouse location: 1024 Industrial Blvd, Houston, TX 77002, USA. Not open on weekends. Customer support: [email protected]."
|
489 |
+
],
|
490 |
+
[
|
491 |
+
"My grandma lives at 2F, 15-3 Shinjuku, Tokyo 160-0022, Japan. Don't use my old phone number anymore!"
|
492 |
+
],
|
493 |
+
[
|
494 |
+
"Delivery address: Apt 9, 88 Queen's Road Central, Central, Hong Kong. Landmark: Opposite IFC Mall."
|
495 |
+
]
|
496 |
+
]
|
497 |
+
}
|
498 |
+
}
|
499 |
+
},
|
500 |
+
"error": null,
|
501 |
+
"input_metadata": null,
|
502 |
+
"meta": {
|
503 |
+
"inputs": {
|
504 |
+
"input": {
|
505 |
+
"name": "input",
|
506 |
+
"position": "bottom",
|
507 |
+
"type": {
|
508 |
+
"type": "<class 'inspect._empty'>"
|
509 |
+
}
|
510 |
+
}
|
511 |
+
},
|
512 |
+
"name": "View DataFrame",
|
513 |
+
"outputs": {},
|
514 |
+
"params": {},
|
515 |
+
"position": {
|
516 |
+
"x": 1083.0,
|
517 |
+
"y": 134.0
|
518 |
+
},
|
519 |
+
"type": "table_view"
|
520 |
+
},
|
521 |
+
"params": {},
|
522 |
+
"status": "done",
|
523 |
+
"title": "View DataFrame"
|
524 |
+
},
|
525 |
+
"dragHandle": ".bg-primary",
|
526 |
+
"height": 200.0,
|
527 |
+
"id": "View DataFrame 2",
|
528 |
+
"position": {
|
529 |
+
"x": 515.0,
|
530 |
+
"y": -135.125
|
531 |
+
},
|
532 |
+
"type": "table_view",
|
533 |
+
"width": 200.0
|
534 |
+
}
|
535 |
+
]
|
536 |
+
}
|
examples/LynxScribe Image Search.lynxkite.json
CHANGED
@@ -14,6 +14,13 @@
|
|
14 |
"target": "LynxScribe Image RAG Builder 1",
|
15 |
"targetHandle": "image_descriptions"
|
16 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
{
|
18 |
"id": "Input chat 1 LynxScribe Image RAG Query 1",
|
19 |
"source": "Input chat 1",
|
@@ -27,13 +34,6 @@
|
|
27 |
"sourceHandle": "output",
|
28 |
"target": "LynxScribe Image RAG Query 1",
|
29 |
"targetHandle": "rag_graph"
|
30 |
-
},
|
31 |
-
{
|
32 |
-
"id": "LynxScribe Image RAG Query 1 LynxScribe Image Result Viewer 1",
|
33 |
-
"source": "LynxScribe Image RAG Query 1",
|
34 |
-
"sourceHandle": "output",
|
35 |
-
"target": "LynxScribe Image Result Viewer 1",
|
36 |
-
"targetHandle": "embedding_similarities"
|
37 |
}
|
38 |
],
|
39 |
"env": "LynxScribe",
|
@@ -292,14 +292,10 @@
|
|
292 |
}
|
293 |
}
|
294 |
},
|
295 |
-
"position": {
|
296 |
-
"x": 1260.0,
|
297 |
-
"y": 166.0
|
298 |
-
},
|
299 |
"type": "basic"
|
300 |
},
|
301 |
"params": {
|
302 |
-
"chat": "Show me a picture about
|
303 |
},
|
304 |
"status": "done",
|
305 |
"title": "Input chat"
|
@@ -316,6 +312,42 @@
|
|
316 |
},
|
317 |
{
|
318 |
"data": {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
319 |
"display": null,
|
320 |
"error": null,
|
321 |
"input_metadata": null,
|
@@ -356,64 +388,26 @@
|
|
356 |
}
|
357 |
},
|
358 |
"position": {
|
359 |
-
"x":
|
360 |
-
"y":
|
361 |
},
|
362 |
"type": "basic"
|
363 |
},
|
364 |
"params": {
|
365 |
-
"top_k":
|
366 |
},
|
367 |
"status": "done",
|
368 |
"title": "LynxScribe Image RAG Query"
|
369 |
},
|
370 |
"dragHandle": ".bg-primary",
|
371 |
-
"height":
|
372 |
"id": "LynxScribe Image RAG Query 1",
|
373 |
"position": {
|
374 |
-
"x":
|
375 |
-
"y": -
|
376 |
},
|
377 |
"type": "basic",
|
378 |
-
"width":
|
379 |
-
},
|
380 |
-
{
|
381 |
-
"data": {
|
382 |
-
"display": "https://storage.googleapis.com/lynxkite_public_data/lynxscribe-images/image-rag-test/bethesda-naval-medical-center-80380_1280.jpg",
|
383 |
-
"error": null,
|
384 |
-
"input_metadata": null,
|
385 |
-
"meta": {
|
386 |
-
"inputs": {
|
387 |
-
"embedding_similarities": {
|
388 |
-
"name": "embedding_similarities",
|
389 |
-
"position": "left",
|
390 |
-
"type": {
|
391 |
-
"type": "<class 'inspect._empty'>"
|
392 |
-
}
|
393 |
-
}
|
394 |
-
},
|
395 |
-
"name": "LynxScribe Image Result Viewer",
|
396 |
-
"outputs": {},
|
397 |
-
"params": {},
|
398 |
-
"position": {
|
399 |
-
"x": 2326.0,
|
400 |
-
"y": 319.0
|
401 |
-
},
|
402 |
-
"type": "image"
|
403 |
-
},
|
404 |
-
"params": {},
|
405 |
-
"status": "done",
|
406 |
-
"title": "LynxScribe Image Result Viewer"
|
407 |
-
},
|
408 |
-
"dragHandle": ".bg-primary",
|
409 |
-
"height": 515.0,
|
410 |
-
"id": "LynxScribe Image Result Viewer 1",
|
411 |
-
"position": {
|
412 |
-
"x": 1657.0,
|
413 |
-
"y": -193.0
|
414 |
-
},
|
415 |
-
"type": "image",
|
416 |
-
"width": 707.0
|
417 |
}
|
418 |
]
|
419 |
}
|
|
|
14 |
"target": "LynxScribe Image RAG Builder 1",
|
15 |
"targetHandle": "image_descriptions"
|
16 |
},
|
17 |
+
{
|
18 |
+
"id": "LynxScribe Image RAG Query 1 LynxScribe Image Result Viewer 1",
|
19 |
+
"source": "LynxScribe Image RAG Query 1",
|
20 |
+
"sourceHandle": "output",
|
21 |
+
"target": "LynxScribe Image Result Viewer 1",
|
22 |
+
"targetHandle": "embedding_similarities"
|
23 |
+
},
|
24 |
{
|
25 |
"id": "Input chat 1 LynxScribe Image RAG Query 1",
|
26 |
"source": "Input chat 1",
|
|
|
34 |
"sourceHandle": "output",
|
35 |
"target": "LynxScribe Image RAG Query 1",
|
36 |
"targetHandle": "rag_graph"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
}
|
38 |
],
|
39 |
"env": "LynxScribe",
|
|
|
292 |
}
|
293 |
}
|
294 |
},
|
|
|
|
|
|
|
|
|
295 |
"type": "basic"
|
296 |
},
|
297 |
"params": {
|
298 |
+
"chat": "Show me a picture about healthy lifestyle"
|
299 |
},
|
300 |
"status": "done",
|
301 |
"title": "Input chat"
|
|
|
312 |
},
|
313 |
{
|
314 |
"data": {
|
315 |
+
"display": "https://storage.googleapis.com/lynxkite_public_data/lynxscribe-images/image-rag-test/food-405521_1280.jpg",
|
316 |
+
"error": null,
|
317 |
+
"input_metadata": null,
|
318 |
+
"meta": {
|
319 |
+
"inputs": {
|
320 |
+
"embedding_similarities": {
|
321 |
+
"name": "embedding_similarities",
|
322 |
+
"position": "left",
|
323 |
+
"type": {
|
324 |
+
"type": "<class 'inspect._empty'>"
|
325 |
+
}
|
326 |
+
}
|
327 |
+
},
|
328 |
+
"name": "LynxScribe Image Result Viewer",
|
329 |
+
"outputs": {},
|
330 |
+
"params": {},
|
331 |
+
"type": "image"
|
332 |
+
},
|
333 |
+
"params": {},
|
334 |
+
"status": "done",
|
335 |
+
"title": "LynxScribe Image Result Viewer"
|
336 |
+
},
|
337 |
+
"dragHandle": ".bg-primary",
|
338 |
+
"height": 1008.0,
|
339 |
+
"id": "LynxScribe Image Result Viewer 1",
|
340 |
+
"position": {
|
341 |
+
"x": 1674.3708499095837,
|
342 |
+
"y": -254.88365280289335
|
343 |
+
},
|
344 |
+
"type": "image",
|
345 |
+
"width": 677.0
|
346 |
+
},
|
347 |
+
{
|
348 |
+
"data": {
|
349 |
+
"__execution_delay": 0.0,
|
350 |
+
"collapsed": null,
|
351 |
"display": null,
|
352 |
"error": null,
|
353 |
"input_metadata": null,
|
|
|
388 |
}
|
389 |
},
|
390 |
"position": {
|
391 |
+
"x": 1611.0,
|
392 |
+
"y": 353.0
|
393 |
},
|
394 |
"type": "basic"
|
395 |
},
|
396 |
"params": {
|
397 |
+
"top_k": "1"
|
398 |
},
|
399 |
"status": "done",
|
400 |
"title": "LynxScribe Image RAG Query"
|
401 |
},
|
402 |
"dragHandle": ".bg-primary",
|
403 |
+
"height": 212.0,
|
404 |
"id": "LynxScribe Image RAG Query 1",
|
405 |
"position": {
|
406 |
+
"x": 1106.0332007233271,
|
407 |
+
"y": -44.51280289330922
|
408 |
},
|
409 |
"type": "basic",
|
410 |
+
"width": 281.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
411 |
}
|
412 |
]
|
413 |
}
|
examples/uploads/task_solver_examples.xlsx
ADDED
Binary file (11.3 kB). View file
|
|
lynxkite-app/src/lynxkite_app/crdt.py
CHANGED
@@ -273,6 +273,7 @@ async def execute(name: str, ws_crdt: pycrdt.Map, ws_pyd: workspace.Workspace, d
|
|
273 |
nc["data"]["status"] = "planned"
|
274 |
# Nodes get a reference to their CRDT maps, so they can update them as the results come in.
|
275 |
np._crdt = nc
|
|
|
276 |
await workspace.execute(ws_pyd)
|
277 |
workspace.save(ws_pyd, path)
|
278 |
print(f"Finished running {name} in {ws_pyd.env}.")
|
|
|
273 |
nc["data"]["status"] = "planned"
|
274 |
# Nodes get a reference to their CRDT maps, so they can update them as the results come in.
|
275 |
np._crdt = nc
|
276 |
+
ws_pyd = ws_pyd.normalize()
|
277 |
await workspace.execute(ws_pyd)
|
278 |
workspace.save(ws_pyd, path)
|
279 |
print(f"Finished running {name} in {ws_pyd.env}.")
|
lynxkite-core/src/lynxkite/core/executors/simple.py
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""A LynxKite executor that simply passes the output of one box to the other."""
|
2 |
+
|
3 |
+
import os
|
4 |
+
from .. import ops
|
5 |
+
from .. import workspace
|
6 |
+
import traceback
|
7 |
+
import inspect
|
8 |
+
import graphlib
|
9 |
+
|
10 |
+
|
11 |
+
def register(env: str):
|
12 |
+
"""Registers the one-by-one executor."""
|
13 |
+
ops.EXECUTORS[env] = lambda ws: execute(ws, ops.CATALOGS[env])
|
14 |
+
|
15 |
+
|
16 |
+
async def await_if_needed(obj):
|
17 |
+
if inspect.isawaitable(obj):
|
18 |
+
return await obj
|
19 |
+
return obj
|
20 |
+
|
21 |
+
|
22 |
+
async def execute(ws: workspace.Workspace, catalog: ops.Catalog):
|
23 |
+
nodes = {n.id: n for n in ws.nodes}
|
24 |
+
dependencies = {n: [] for n in nodes}
|
25 |
+
in_edges = {n: {} for n in nodes}
|
26 |
+
for e in ws.edges:
|
27 |
+
dependencies[e.target].append(e.source)
|
28 |
+
assert e.targetHandle not in in_edges[e.target], f"Duplicate input for {e.target}"
|
29 |
+
in_edges[e.target][e.targetHandle] = e.source, e.sourceHandle
|
30 |
+
outputs = {}
|
31 |
+
ts = graphlib.TopologicalSorter(dependencies)
|
32 |
+
for node_id in ts.static_order():
|
33 |
+
node = nodes[node_id]
|
34 |
+
op = catalog[node.data.title]
|
35 |
+
params = {**node.data.params}
|
36 |
+
node.publish_started()
|
37 |
+
try:
|
38 |
+
inputs = []
|
39 |
+
missing = []
|
40 |
+
for i in op.inputs.values():
|
41 |
+
edges = in_edges[node_id]
|
42 |
+
if i.name in edges and edges[i.name] in outputs:
|
43 |
+
inputs.append(outputs[edges[i.name]])
|
44 |
+
else:
|
45 |
+
missing.append(i.name)
|
46 |
+
if missing:
|
47 |
+
node.publish_error(f"Missing input: {', '.join(missing)}")
|
48 |
+
continue
|
49 |
+
result = op(*inputs, **params)
|
50 |
+
result.output = await await_if_needed(result.output)
|
51 |
+
result.display = await await_if_needed(result.display)
|
52 |
+
if len(op.outputs) == 1:
|
53 |
+
[output] = list(op.outputs.values())
|
54 |
+
outputs[node_id, output.name] = result.output
|
55 |
+
elif len(op.outputs) > 1:
|
56 |
+
assert type(result.output) is dict, "An op with multiple outputs must return a dict"
|
57 |
+
for output in op.outputs.values():
|
58 |
+
outputs[node_id, output.name] = result.output[output.name]
|
59 |
+
node.publish_result(result)
|
60 |
+
except Exception as e:
|
61 |
+
if not os.environ.get("LYNXKITE_SUPPRESS_OP_ERRORS"):
|
62 |
+
traceback.print_exc()
|
63 |
+
node.publish_error(e)
|
64 |
+
return outputs
|
lynxkite-core/src/lynxkite/core/ops.py
CHANGED
@@ -144,6 +144,7 @@ def _param_to_type(name, value, type):
|
|
144 |
assert value != "", f"{name} is unset."
|
145 |
return float(value)
|
146 |
if isinstance(type, enum.EnumMeta):
|
|
|
147 |
return type[value]
|
148 |
if isinstance(type, types.UnionType):
|
149 |
match type.__args__:
|
|
|
144 |
assert value != "", f"{name} is unset."
|
145 |
return float(value)
|
146 |
if isinstance(type, enum.EnumMeta):
|
147 |
+
assert value in type.__members__, f"{value} is not an option for {name}."
|
148 |
return type[value]
|
149 |
if isinstance(type, types.UnionType):
|
150 |
match type.__args__:
|
lynxkite-core/src/lynxkite/core/workspace.py
CHANGED
@@ -97,6 +97,25 @@ class Workspace(BaseConfig):
|
|
97 |
edges: list[WorkspaceEdge] = dataclasses.field(default_factory=list)
|
98 |
_crdt: pycrdt.Map
|
99 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
100 |
|
101 |
async def execute(ws: Workspace):
|
102 |
if ws.env in ops.EXECUTORS:
|
|
|
97 |
edges: list[WorkspaceEdge] = dataclasses.field(default_factory=list)
|
98 |
_crdt: pycrdt.Map
|
99 |
|
100 |
+
def normalize(self):
|
101 |
+
if self.env not in ops.CATALOGS:
|
102 |
+
return self
|
103 |
+
catalog = ops.CATALOGS[self.env]
|
104 |
+
_ops = {n.id: catalog[n.data.title] for n in self.nodes if n.data.title in catalog}
|
105 |
+
valid_targets = set(
|
106 |
+
(n.id, h) for n in self.nodes for h in _ops[n.id].inputs if n.id in _ops
|
107 |
+
)
|
108 |
+
valid_sources = set(
|
109 |
+
(n.id, h) for n in self.nodes for h in _ops[n.id].outputs if n.id in _ops
|
110 |
+
)
|
111 |
+
edges = [
|
112 |
+
edge
|
113 |
+
for edge in self.edges
|
114 |
+
if (edge.source, edge.sourceHandle) in valid_sources
|
115 |
+
and (edge.target, edge.targetHandle) in valid_targets
|
116 |
+
]
|
117 |
+
return self.model_copy(update={"edges": edges})
|
118 |
+
|
119 |
|
120 |
async def execute(ws: Workspace):
|
121 |
if ws.env in ops.EXECUTORS:
|
lynxkite-lynxscribe/src/lynxkite_lynxscribe/lynxscribe_ops.py
CHANGED
@@ -28,7 +28,7 @@ from lynxscribe.components.chat.processors import (
|
|
28 |
TruncateHistory,
|
29 |
)
|
30 |
from lynxscribe.components.chat.api import ChatAPI
|
31 |
-
from lynxscribe.core.models.prompts import ChatCompletionPrompt
|
32 |
from lynxscribe.components.rag.loaders import FAQTemplateLoader
|
33 |
|
34 |
from lynxkite.core import ops
|
@@ -56,6 +56,11 @@ class RAGVersion(Enum):
|
|
56 |
V2 = "v2"
|
57 |
|
58 |
|
|
|
|
|
|
|
|
|
|
|
59 |
class RAGTemplate(BaseModel):
|
60 |
"""
|
61 |
Model for RAG templates consisting of three tables: they are connected via scenario names.
|
@@ -365,6 +370,10 @@ def ls_save_rag_graph(
|
|
365 |
@ops.input_position(rag_graph="bottom")
|
366 |
@op("LynxScribe Image RAG Query")
|
367 |
async def search_context(rag_graph, text, *, top_k=3):
|
|
|
|
|
|
|
|
|
368 |
message = text["text"]
|
369 |
rag_graph = rag_graph[0]["rag_graph"]
|
370 |
|
@@ -382,7 +391,9 @@ async def search_context(rag_graph, text, *, top_k=3):
|
|
382 |
description = emb_sim.embedding.document
|
383 |
result_list.append({"image_url": image_url, "score": score, "description": description})
|
384 |
|
385 |
-
|
|
|
|
|
386 |
|
387 |
|
388 |
@op("LynxScribe Image Result Viewer", view="image")
|
@@ -672,6 +683,116 @@ def chat_processor(processor, *, _ctx: one_by_one.Context):
|
|
672 |
return {"chat_processor": chat_processor, **cfg}
|
673 |
|
674 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
675 |
@output_on_top
|
676 |
@op("Truncate history")
|
677 |
def truncate_history(*, max_tokens=10000):
|
@@ -718,6 +839,21 @@ def input_chat(*, chat: str):
|
|
718 |
return {"text": chat}
|
719 |
|
720 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
721 |
@op("View", view="table_view")
|
722 |
def view(input):
|
723 |
columns = [str(c) for c in input.keys() if not str(c).startswith("_")]
|
|
|
28 |
TruncateHistory,
|
29 |
)
|
30 |
from lynxscribe.components.chat.api import ChatAPI
|
31 |
+
from lynxscribe.core.models.prompts import ChatCompletionPrompt, Message
|
32 |
from lynxscribe.components.rag.loaders import FAQTemplateLoader
|
33 |
|
34 |
from lynxkite.core import ops
|
|
|
56 |
V2 = "v2"
|
57 |
|
58 |
|
59 |
+
class MessageRole(Enum):
|
60 |
+
SYSTEM = "system"
|
61 |
+
USER = "user"
|
62 |
+
|
63 |
+
|
64 |
class RAGTemplate(BaseModel):
|
65 |
"""
|
66 |
Model for RAG templates consisting of three tables: they are connected via scenario names.
|
|
|
370 |
@ops.input_position(rag_graph="bottom")
|
371 |
@op("LynxScribe Image RAG Query")
|
372 |
async def search_context(rag_graph, text, *, top_k=3):
|
373 |
+
"""
|
374 |
+
top_k: which results we are showing (TODO: when the image viewer is
|
375 |
+
updated w pager, change back to top k)
|
376 |
+
"""
|
377 |
message = text["text"]
|
378 |
rag_graph = rag_graph[0]["rag_graph"]
|
379 |
|
|
|
391 |
description = emb_sim.embedding.document
|
392 |
result_list.append({"image_url": image_url, "score": score, "description": description})
|
393 |
|
394 |
+
real_k = min(top_k, len(result_list) - 1)
|
395 |
+
|
396 |
+
return {"embedding_similarities": [result_list[real_k]]}
|
397 |
|
398 |
|
399 |
@op("LynxScribe Image Result Viewer", view="image")
|
|
|
683 |
return {"chat_processor": chat_processor, **cfg}
|
684 |
|
685 |
|
686 |
+
@output_on_top
|
687 |
+
@op("LynxScribe Message")
|
688 |
+
def lynxscribe_message(
|
689 |
+
*, prompt_role: MessageRole = MessageRole.SYSTEM, prompt_content: ops.LongStr
|
690 |
+
):
|
691 |
+
return_message = Message(role=prompt_role.value, content=prompt_content.strip())
|
692 |
+
return {"prompt_message": return_message}
|
693 |
+
|
694 |
+
|
695 |
+
@op("Read Excel")
|
696 |
+
def read_excel(*, file_path: str, sheet_name: str = "Sheet1", columns: str = ""):
|
697 |
+
"""
|
698 |
+
Reads an Excel file and returns the content of the specified sheet.
|
699 |
+
The columns parameter can be used to specify which columns to include in the output.
|
700 |
+
If not specified, all columns will be included (separate the values by comma).
|
701 |
+
|
702 |
+
TODO: more general: several input/output versions.
|
703 |
+
"""
|
704 |
+
df = pd.read_excel(file_path, sheet_name=sheet_name)
|
705 |
+
if columns:
|
706 |
+
columns = [c.strip() for c in columns.split(",") if c.strip()]
|
707 |
+
columns = [c for c in columns if c in df.columns]
|
708 |
+
if len(columns) == 0:
|
709 |
+
raise ValueError("No valid columns specified.")
|
710 |
+
df = df[columns].copy()
|
711 |
+
return {"dataframe": df}
|
712 |
+
|
713 |
+
|
714 |
+
@ops.input_position(system_prompt="bottom", instruction_prompt="bottom", dataframe="left")
|
715 |
+
@op("LynxScribe Task Solver")
|
716 |
+
@mem.cache
|
717 |
+
async def ls_task_solver(
|
718 |
+
system_prompt,
|
719 |
+
instruction_prompt,
|
720 |
+
dataframe,
|
721 |
+
*,
|
722 |
+
llm_interface: str = "openai",
|
723 |
+
llm_model_name: str = "gpt-4o",
|
724 |
+
new_column_names: str = "processed_field",
|
725 |
+
# api_key_name: str = "OPENAI_API_KEY",
|
726 |
+
):
|
727 |
+
"""
|
728 |
+
Solving the described task on a data frame and put the results into a new column.
|
729 |
+
|
730 |
+
If there are multiple new_column_names provided, the structured dictionary output
|
731 |
+
will be split into multiple columns.
|
732 |
+
"""
|
733 |
+
|
734 |
+
# handling inputs
|
735 |
+
system_message = system_prompt[0]["prompt_message"]
|
736 |
+
instruction_message = instruction_prompt[0]["prompt_message"]
|
737 |
+
df = dataframe["dataframe"]
|
738 |
+
|
739 |
+
# preparing output
|
740 |
+
out_df = df.copy()
|
741 |
+
|
742 |
+
# connecting to the LLM
|
743 |
+
llm_params = {"name": llm_interface}
|
744 |
+
# if api_key_name:
|
745 |
+
# llm_params["api_key"] = os.getenv(api_key_name)
|
746 |
+
llm = get_llm_engine(**llm_params)
|
747 |
+
|
748 |
+
# getting the list of fieldnames used in the instruction message
|
749 |
+
fieldnames = []
|
750 |
+
for pot_fieldname in df.columns:
|
751 |
+
if "{" + pot_fieldname + "}" in instruction_message.content:
|
752 |
+
fieldnames.append(pot_fieldname)
|
753 |
+
|
754 |
+
# generate a list of instruction messages (from fieldnames)
|
755 |
+
# each row of the df is a separate instruction message
|
756 |
+
# TODO: make it fast for large dataframes
|
757 |
+
instruction_messages = []
|
758 |
+
for i in range(len(df)):
|
759 |
+
instruction_message_i = deepcopy(instruction_message)
|
760 |
+
for fieldname in fieldnames:
|
761 |
+
instruction_message_i.content = instruction_message_i.content.replace(
|
762 |
+
"{" + fieldname + "}", str(df.iloc[i][fieldname])
|
763 |
+
)
|
764 |
+
instruction_messages.append(instruction_message_i)
|
765 |
+
|
766 |
+
# generate completition prompt
|
767 |
+
completion_prompts = [
|
768 |
+
ChatCompletionPrompt(
|
769 |
+
model=llm_model_name,
|
770 |
+
messages=[system_message, instruction_message_j],
|
771 |
+
)
|
772 |
+
for instruction_message_j in instruction_messages
|
773 |
+
]
|
774 |
+
|
775 |
+
# get the answers
|
776 |
+
tasks = [llm.acreate_completion(completion_prompt=_prompt) for _prompt in completion_prompts]
|
777 |
+
out_completions = await asyncio.gather(*tasks)
|
778 |
+
|
779 |
+
# answer post-processing: 1 vs more columns
|
780 |
+
col_list = [_c.strip() for _c in new_column_names.split(",") if _c.strip()]
|
781 |
+
if len(col_list) == 0:
|
782 |
+
raise ValueError("No valid column names specified.")
|
783 |
+
elif len(col_list) == 1:
|
784 |
+
out_df[col_list[0]] = [result.choices[0].message.content for result in out_completions]
|
785 |
+
else:
|
786 |
+
answers = [
|
787 |
+
dictionary_corrector(result.choices[0].message.content, expected_keys=col_list)
|
788 |
+
for result in out_completions
|
789 |
+
]
|
790 |
+
for i, col in enumerate(col_list):
|
791 |
+
out_df[col] = [answer[col] for answer in answers]
|
792 |
+
|
793 |
+
return {"dataframe": out_df}
|
794 |
+
|
795 |
+
|
796 |
@output_on_top
|
797 |
@op("Truncate history")
|
798 |
def truncate_history(*, max_tokens=10000):
|
|
|
839 |
return {"text": chat}
|
840 |
|
841 |
|
842 |
+
@ops.input_position(input="bottom")
|
843 |
+
@op("View DataFrame", view="table_view")
|
844 |
+
def view_df(input):
|
845 |
+
df = input[0]["dataframe"]
|
846 |
+
v = {
|
847 |
+
"dataframes": {
|
848 |
+
"df": {
|
849 |
+
"columns": [str(c) for c in df.columns],
|
850 |
+
"data": df.values.tolist(),
|
851 |
+
}
|
852 |
+
}
|
853 |
+
}
|
854 |
+
return v
|
855 |
+
|
856 |
+
|
857 |
@op("View", view="table_view")
|
858 |
def view(input):
|
859 |
columns = [str(c) for c in input.keys() if not str(c).startswith("_")]
|
lynxkite-pillow-example/src/lynxkite_pillow_example/__init__.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
"""Demo for how easily we can provide a UI for popular open-source tools."""
|
2 |
|
3 |
from lynxkite.core import ops
|
4 |
-
from lynxkite.core.executors import
|
5 |
from PIL import Image, ImageFilter
|
6 |
import base64
|
7 |
import fsspec
|
@@ -9,7 +9,7 @@ import io
|
|
9 |
|
10 |
ENV = "Pillow"
|
11 |
op = ops.op_registration(ENV)
|
12 |
-
|
13 |
|
14 |
|
15 |
@op("Open image")
|
|
|
1 |
"""Demo for how easily we can provide a UI for popular open-source tools."""
|
2 |
|
3 |
from lynxkite.core import ops
|
4 |
+
from lynxkite.core.executors import simple
|
5 |
from PIL import Image, ImageFilter
|
6 |
import base64
|
7 |
import fsspec
|
|
|
9 |
|
10 |
ENV = "Pillow"
|
11 |
op = ops.op_registration(ENV)
|
12 |
+
simple.register(ENV)
|
13 |
|
14 |
|
15 |
@op("Open image")
|