Spaces:
Running
Running
Export the constructed Chat API through an HTTP endpoint.
Browse files- server/executors/one_by_one.py +1 -0
- server/lynxscribe_ops.py +35 -0
- server/main.py +10 -1
server/executors/one_by_one.py
CHANGED
@@ -140,3 +140,4 @@ def execute(ws, catalog, cache=None):
|
|
140 |
else:
|
141 |
tasks.setdefault(edge.target, []).extend(results)
|
142 |
tasks = next_stage
|
|
|
|
140 |
else:
|
141 |
tasks.setdefault(edge.target, []).extend(results)
|
142 |
tasks = next_stage
|
143 |
+
return contexts
|
server/lynxscribe_ops.py
CHANGED
@@ -163,3 +163,38 @@ def view(input):
|
|
163 |
}}
|
164 |
}
|
165 |
return v
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
163 |
}}
|
164 |
}
|
165 |
return v
|
166 |
+
|
167 |
+
async def api_service(request):
|
168 |
+
'''
|
169 |
+
Serves a chat endpoint that matches LynxScribe's interface.
|
170 |
+
To access it you need to add the "module" and "workspace"
|
171 |
+
parameters.
|
172 |
+
The workspace must contain exactly one "Chat API" node.
|
173 |
+
|
174 |
+
curl -X POST ${LYNXKITE_URL}/api/service \
|
175 |
+
-H "Content-Type: application/json" \
|
176 |
+
-d '{
|
177 |
+
"module": "server.lynxscribe_ops",
|
178 |
+
"workspace": "LynxScribe demo",
|
179 |
+
"session_id": "b43215a0-428f-11ef-9454-0242ac120002",
|
180 |
+
"question": "what does the fox say",
|
181 |
+
"history": [],
|
182 |
+
"user_id": "x",
|
183 |
+
"meta_inputs": {}
|
184 |
+
}'
|
185 |
+
'''
|
186 |
+
import pathlib
|
187 |
+
from . import workspace
|
188 |
+
DATA_PATH = pathlib.Path.cwd() / 'data'
|
189 |
+
path = DATA_PATH / request['workspace']
|
190 |
+
assert path.is_relative_to(DATA_PATH)
|
191 |
+
assert path.exists(), f'Workspace {path} does not exist'
|
192 |
+
ws = workspace.load(path)
|
193 |
+
contexts = ops.EXECUTORS[ENV](ws)
|
194 |
+
nodes = [op for op in ws.nodes if op.data.title == 'Chat API']
|
195 |
+
[node] = nodes
|
196 |
+
context = contexts[node.id]
|
197 |
+
chat_api = context.last_result['chat_api']
|
198 |
+
request = ChatAPIRequest(session_id=request['session_id'], question=request['question'], history=request['history'])
|
199 |
+
response = await chat_api.answer(request)
|
200 |
+
return response
|
server/main.py
CHANGED
@@ -1,15 +1,18 @@
|
|
1 |
import dataclasses
|
2 |
import fastapi
|
|
|
3 |
import pathlib
|
4 |
import pkgutil
|
5 |
from . import ops
|
6 |
from . import workspace
|
7 |
|
8 |
here = pathlib.Path(__file__).parent
|
|
|
9 |
for _, name, _ in pkgutil.iter_modules([str(here)]):
|
10 |
if name.endswith('_ops') and not name.startswith('test_'):
|
11 |
print(f'Importing {name}')
|
12 |
-
|
|
|
13 |
|
14 |
app = fastapi.FastAPI()
|
15 |
|
@@ -67,3 +70,9 @@ def make_dir(req: dict):
|
|
67 |
assert not path.exists()
|
68 |
path.mkdir()
|
69 |
return list_dir(path.parent)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import dataclasses
|
2 |
import fastapi
|
3 |
+
import importlib
|
4 |
import pathlib
|
5 |
import pkgutil
|
6 |
from . import ops
|
7 |
from . import workspace
|
8 |
|
9 |
here = pathlib.Path(__file__).parent
|
10 |
+
lynxkite_modules = {}
|
11 |
for _, name, _ in pkgutil.iter_modules([str(here)]):
|
12 |
if name.endswith('_ops') and not name.startswith('test_'):
|
13 |
print(f'Importing {name}')
|
14 |
+
name = f'server.{name}'
|
15 |
+
lynxkite_modules[name] = importlib.import_module(name)
|
16 |
|
17 |
app = fastapi.FastAPI()
|
18 |
|
|
|
70 |
assert not path.exists()
|
71 |
path.mkdir()
|
72 |
return list_dir(path.parent)
|
73 |
+
|
74 |
+
@app.post("/api/service")
|
75 |
+
async def service(req: dict):
|
76 |
+
'''Executors can provide extra HTTP APIs through the /api/service endpoint.'''
|
77 |
+
module = lynxkite_modules[req['module']]
|
78 |
+
return await module.api_service(req)
|