@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 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(uvicornExec);
85
- const command = useVenv ? uvicornExec : 'uvicorn';
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, ['server:app', '--port', PYTHON_PORT.toString()], {
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 pipCommand = process.platform === 'win32'
121
- ? path.join(venvPath, 'Scripts', 'pip')
122
- : path.join(venvPath, 'bin', 'pip');
120
+ const pythonCommand = process.platform === 'win32'
121
+ ? path.join(venvPath, 'Scripts', 'python')
122
+ : path.join(venvPath, 'bin', 'python');
123
123
 
124
- await execAsync(`${pipCommand} install fastapi uvicorn laya`, { cwd: __dirname });
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@felipenmoura/laya-node",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Node wrapper for laya text classification",
5
5
  "license": "MIT",
6
6
  "author": "Felipe N. Moura",
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
- kwargs["max_len"] = max_len
28
-
29
- result = router.predict(req.state, req.questions, **kwargs)
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")