Spaces:
Running
Running
Start adding LLM logic boxes. Nicer tables.
Browse files- server/llm_ops.py +65 -0
- server/main.py +2 -1
- web/src/NodeParameter.svelte +1 -3
- web/src/NodeSearch.svelte +1 -1
- web/src/NodeWithTableView.svelte +19 -14
server/llm_ops.py
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
'''For specifying an LLM agent logic flow.'''
|
2 |
+
from . import ops
|
3 |
+
import json
|
4 |
+
import openai
|
5 |
+
import pandas as pd
|
6 |
+
|
7 |
+
client = openai.OpenAI(base_url="http://localhost:11434/v1")
|
8 |
+
CACHE = {}
|
9 |
+
|
10 |
+
def chat(*args, **kwargs):
|
11 |
+
key = json.dumps({'args': args, 'kwargs': kwargs})
|
12 |
+
if key not in CACHE:
|
13 |
+
completion = client.chat.completions.create(*args, **kwargs)
|
14 |
+
CACHE[key] = [c.message.content for c in completion.choices]
|
15 |
+
return CACHE[key]
|
16 |
+
|
17 |
+
@ops.op("Input")
|
18 |
+
def input(*, filename: str, key: str):
|
19 |
+
return pd.read_csv(filename).rename(columns={key: 'text'})
|
20 |
+
|
21 |
+
@ops.op("Create prompt")
|
22 |
+
def create_prompt(input, *, template: str):
|
23 |
+
assert template, 'Please specify the template. Refer to columns using their names in uppercase.'
|
24 |
+
df = input.copy()
|
25 |
+
prompts = []
|
26 |
+
for i, row in df.iterrows():
|
27 |
+
p = template
|
28 |
+
for c in df.columns:
|
29 |
+
p = p.replace(c.upper(), str(row[c]))
|
30 |
+
prompts.append(p)
|
31 |
+
df['prompt'] = p
|
32 |
+
return df
|
33 |
+
|
34 |
+
|
35 |
+
@ops.op("Ask LLM")
|
36 |
+
def ask_llm(input, *, model: str, choices: list = None, max_tokens: int = 100):
|
37 |
+
assert model, 'Please specify the model.'
|
38 |
+
assert 'prompt' in input.columns, 'Please create the prompt first.'
|
39 |
+
df = input.copy()
|
40 |
+
g = {}
|
41 |
+
if choices:
|
42 |
+
g['extra_body'] = {
|
43 |
+
"guided_choice": choices.split()
|
44 |
+
}
|
45 |
+
for i, row in df.iterrows():
|
46 |
+
[res] = chat(
|
47 |
+
model=model,
|
48 |
+
max_tokens=max_tokens,
|
49 |
+
messages=[
|
50 |
+
{"role": "user", "content": row['text']},
|
51 |
+
],
|
52 |
+
**g,
|
53 |
+
)
|
54 |
+
df.loc[i, 'response'] = res
|
55 |
+
return df
|
56 |
+
|
57 |
+
@ops.op("View", view="table_view")
|
58 |
+
def view(input):
|
59 |
+
v = {
|
60 |
+
'dataframes': { 'df': {
|
61 |
+
'columns': [str(c) for c in input.columns],
|
62 |
+
'data': input.values.tolist(),
|
63 |
+
}}
|
64 |
+
}
|
65 |
+
return v
|
server/main.py
CHANGED
@@ -5,8 +5,9 @@ from . import ops
|
|
5 |
from . import workspace
|
6 |
from . import lynxkite_ops
|
7 |
# from . import networkx_ops
|
8 |
-
from . import pytorch_model_ops
|
9 |
# from . import lynxscribe_ops
|
|
|
10 |
|
11 |
app = fastapi.FastAPI()
|
12 |
|
|
|
5 |
from . import workspace
|
6 |
from . import lynxkite_ops
|
7 |
# from . import networkx_ops
|
8 |
+
# from . import pytorch_model_ops
|
9 |
# from . import lynxscribe_ops
|
10 |
+
from . import llm_ops
|
11 |
|
12 |
app = fastapi.FastAPI()
|
13 |
|
web/src/NodeParameter.svelte
CHANGED
@@ -38,9 +38,7 @@
|
|
38 |
font-size: 10px;
|
39 |
letter-spacing: 0.05em;
|
40 |
margin-left: 10px;
|
41 |
-
background:
|
42 |
-
background: oklch(95% 0.2 55);
|
43 |
-
background:var(--bs-border-color);
|
44 |
width: fit-content;
|
45 |
padding: 2px 8px;
|
46 |
border-radius: 4px 4px 0 0;
|
|
|
38 |
font-size: 10px;
|
39 |
letter-spacing: 0.05em;
|
40 |
margin-left: 10px;
|
41 |
+
background: var(--bs-border-color);
|
|
|
|
|
42 |
width: fit-content;
|
43 |
padding: 2px 8px;
|
44 |
border-radius: 4px 4px 0 0;
|
web/src/NodeSearch.svelte
CHANGED
@@ -8,7 +8,7 @@
|
|
8 |
let hits = Object.values(boxes).map(box => ({item: box}));
|
9 |
let selectedIndex = 0;
|
10 |
onMount(() => searchBox.focus());
|
11 |
-
$: fuse = new Fuse(boxes, {
|
12 |
keys: ['name']
|
13 |
})
|
14 |
function onInput() {
|
|
|
8 |
let hits = Object.values(boxes).map(box => ({item: box}));
|
9 |
let selectedIndex = 0;
|
10 |
onMount(() => searchBox.focus());
|
11 |
+
$: fuse = new Fuse(Object.values(boxes), {
|
12 |
keys: ['name']
|
13 |
})
|
14 |
function onInput() {
|
web/src/NodeWithTableView.svelte
CHANGED
@@ -1,29 +1,35 @@
|
|
1 |
<script lang="ts">
|
2 |
import { type NodeProps } from '@xyflow/svelte';
|
|
|
3 |
import LynxKiteNode from './LynxKiteNode.svelte';
|
4 |
type $$Props = NodeProps;
|
5 |
export let data: $$Props['data'];
|
6 |
const open = {};
|
|
|
7 |
</script>
|
8 |
|
9 |
<LynxKiteNode {...$$props}>
|
10 |
{#if data.view}
|
11 |
{#each Object.entries(data.view.dataframes) as [name, df]}
|
12 |
-
<div class="df-head" on:click={() => open[name] = !open[name]}>{name}</div>
|
13 |
-
{#if open[name]}
|
14 |
-
<table>
|
15 |
-
<
|
16 |
-
{#each df.columns as column}
|
17 |
-
<th>{column}</th>
|
18 |
-
{/each}
|
19 |
-
</tr>
|
20 |
-
{#each df.data as row}
|
21 |
<tr>
|
22 |
-
{#each
|
23 |
-
<
|
24 |
{/each}
|
25 |
</tr>
|
26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
</table>
|
28 |
{/if}
|
29 |
{/each}
|
@@ -43,7 +49,6 @@
|
|
43 |
cursor: pointer;
|
44 |
}
|
45 |
table {
|
46 |
-
|
47 |
-
border-collapse: collapse;
|
48 |
}
|
49 |
</style>
|
|
|
1 |
<script lang="ts">
|
2 |
import { type NodeProps } from '@xyflow/svelte';
|
3 |
+
import { Tabulator } from 'tabulator-tables';
|
4 |
import LynxKiteNode from './LynxKiteNode.svelte';
|
5 |
type $$Props = NodeProps;
|
6 |
export let data: $$Props['data'];
|
7 |
const open = {};
|
8 |
+
$: single = data.view?.dataframes && Object.keys(data.view.dataframes).length === 1;
|
9 |
</script>
|
10 |
|
11 |
<LynxKiteNode {...$$props}>
|
12 |
{#if data.view}
|
13 |
{#each Object.entries(data.view.dataframes) as [name, df]}
|
14 |
+
{#if !single}<div class="df-head" on:click={() => open[name] = !open[name]}>{name}</div>{/if}
|
15 |
+
{#if single || open[name]}
|
16 |
+
<table class="table table-striped">
|
17 |
+
<thead>
|
|
|
|
|
|
|
|
|
|
|
18 |
<tr>
|
19 |
+
{#each df.columns as column}
|
20 |
+
<th>{column}</th>
|
21 |
{/each}
|
22 |
</tr>
|
23 |
+
</thead>
|
24 |
+
<tbody>
|
25 |
+
{#each df.data as row}
|
26 |
+
<tr>
|
27 |
+
{#each row as cell}
|
28 |
+
<td><div class="text-truncate">{cell}</div></td>
|
29 |
+
{/each}
|
30 |
+
</tr>
|
31 |
+
{/each}
|
32 |
+
</tbody>
|
33 |
</table>
|
34 |
{/if}
|
35 |
{/each}
|
|
|
49 |
cursor: pointer;
|
50 |
}
|
51 |
table {
|
52 |
+
table-layout: fixed;
|
|
|
53 |
}
|
54 |
</style>
|