@felipenmoura/laya-node 1.0.2 → 1.0.3
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.
- package/README.md +1 -1
- package/index.js +26 -5
- package/install.js +4 -4
- package/package.json +1 -1
- package/server.py +17 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Laya Node Wrapper
|
|
2
2
|
|
|
3
|
-
The Node.js wrapper for running the [Laya Text Classification Model](https://huggingface.co/convaiinnovations/laya).
|
|
3
|
+
The Node.js wrapper for running the [Laya Text Classification Model](https://huggingface.co/convaiinnovations/laya), the multilingual, non-autoregressive System 1 decision model for AI (for artificial inteligence).
|
|
4
4
|
|
|
5
5
|
Laya is a non-autoregressive decision model designed for text classification, email triage, and moderation. This package exposes Laya's API through an Express HTTP server, allowing you to easily integrate it into your Node.js applications while matching the exact Python API structure natively.
|
|
6
6
|
|
package/index.js
CHANGED
|
@@ -81,16 +81,27 @@ const uvicornExec = process.platform === 'win32'
|
|
|
81
81
|
: path.join(venvPath, 'bin', 'uvicorn');
|
|
82
82
|
|
|
83
83
|
// Check if venv exists, otherwise fallback to system python
|
|
84
|
-
const useVenv = fs.existsSync(
|
|
85
|
-
const command = useVenv ?
|
|
84
|
+
const useVenv = fs.existsSync(pythonExec);
|
|
85
|
+
const command = useVenv ? pythonExec : 'python3';
|
|
86
|
+
const args = ['-m', 'uvicorn', 'server:app', '--port', PYTHON_PORT.toString()];
|
|
86
87
|
|
|
87
|
-
console.log(`Starting python server via ${command}...`);
|
|
88
|
+
console.log(`Starting python server via ${command} ${args.join(' ')}...`);
|
|
88
89
|
|
|
89
|
-
const pythonProcess = spawn(command,
|
|
90
|
+
const pythonProcess = spawn(command, args, {
|
|
90
91
|
cwd: __dirname,
|
|
91
92
|
stdio: ['ignore', 'pipe', 'pipe']
|
|
92
93
|
});
|
|
93
94
|
|
|
95
|
+
pythonProcess.on('error', (err) => {
|
|
96
|
+
if (err.code === 'ENOENT') {
|
|
97
|
+
console.error(`\n\x1b[31m✖ Error: Could not find Python executable or uvicorn.\x1b[0m`);
|
|
98
|
+
console.error(`\x1b[33mPlease ensure you have run the setup or installed dependencies in the virtual environment.\x1b[0m\n`);
|
|
99
|
+
} else {
|
|
100
|
+
console.error(`\n\x1b[31m✖ Failed to start Python server:\x1b[0m ${err.message}\n`);
|
|
101
|
+
}
|
|
102
|
+
process.exit(1);
|
|
103
|
+
});
|
|
104
|
+
|
|
94
105
|
let warmedUp = false;
|
|
95
106
|
async function warmupModel() {
|
|
96
107
|
if (warmedUp) return;
|
|
@@ -175,7 +186,7 @@ app.use('/', createProxyMiddleware({
|
|
|
175
186
|
}
|
|
176
187
|
}));
|
|
177
188
|
|
|
178
|
-
app.listen(PORT, () => {
|
|
189
|
+
const server = app.listen(PORT, () => {
|
|
179
190
|
console.log(`Laya Node wrapper is running on http://localhost:${PORT}`);
|
|
180
191
|
if (SECRET_API_KEY) {
|
|
181
192
|
console.log(`API Key protection is ENABLED. Please send "Authorization: Bearer <your-key>"`);
|
|
@@ -203,6 +214,16 @@ app.listen(PORT, () => {
|
|
|
203
214
|
console.log(`\x1b[1mExample Request:\x1b[0m\n${curlExample}`);
|
|
204
215
|
});
|
|
205
216
|
|
|
217
|
+
server.on('error', (err) => {
|
|
218
|
+
if (err.code === 'EADDRINUSE') {
|
|
219
|
+
console.error(`\n\x1b[31m✖ Error: Port ${PORT} is already in use.\x1b[0m`);
|
|
220
|
+
console.error(`\x1b[33mPlease ensure no other process is using this port, or specify a different PORT environment variable.\x1b[0m\n`);
|
|
221
|
+
process.exit(1);
|
|
222
|
+
} else {
|
|
223
|
+
console.error(`\n\x1b[31m✖ Server error:\x1b[0m`, err.message, '\n');
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
206
227
|
// Clean up child process on exit
|
|
207
228
|
process.on('SIGINT', () => {
|
|
208
229
|
pythonProcess.kill('SIGINT');
|
package/install.js
CHANGED
|
@@ -117,11 +117,11 @@ async function runSetup() {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
spinner.text = 'Installing dependencies (laya, fastapi, uvicorn)...';
|
|
120
|
-
const
|
|
121
|
-
? path.join(venvPath, 'Scripts', '
|
|
122
|
-
: path.join(venvPath, 'bin', '
|
|
120
|
+
const pythonCommand = process.platform === 'win32'
|
|
121
|
+
? path.join(venvPath, 'Scripts', 'python')
|
|
122
|
+
: path.join(venvPath, 'bin', 'python');
|
|
123
123
|
|
|
124
|
-
await execAsync(
|
|
124
|
+
await execAsync(`"${pythonCommand}" -m pip install fastapi uvicorn laya`, { cwd: __dirname });
|
|
125
125
|
spinner.succeed('Setup complete!');
|
|
126
126
|
} catch (error) {
|
|
127
127
|
spinner.fail('Setup failed during python environment creation or pip install.');
|
package/package.json
CHANGED
package/server.py
CHANGED
|
@@ -24,9 +24,23 @@ def predict(req: PredictRequest):
|
|
|
24
24
|
kwargs["model"] = req.model
|
|
25
25
|
|
|
26
26
|
max_len = req.max_len if req.max_len is not None else int(os.getenv("MAX_LEN", 8192))
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
|
|
28
|
+
# Manually route to support max_len since Router.predict doesn't accept it
|
|
29
|
+
decision = router.route(req.state, req.questions, **kwargs)
|
|
30
|
+
agent = router.load(decision["model"])
|
|
31
|
+
|
|
32
|
+
original_max_len = agent.cfg.get("max_len")
|
|
33
|
+
agent.cfg["max_len"] = max_len
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
result = agent.system_one(req.state, req.questions)
|
|
37
|
+
result["routing"] = dict(decision)
|
|
38
|
+
finally:
|
|
39
|
+
if original_max_len is not None:
|
|
40
|
+
agent.cfg["max_len"] = original_max_len
|
|
41
|
+
elif "max_len" in agent.cfg:
|
|
42
|
+
del agent.cfg["max_len"]
|
|
43
|
+
|
|
30
44
|
return result
|
|
31
45
|
|
|
32
46
|
@app.get("/health")
|