From a9a464e2a5f5a07052078eb3446587545f31065a Mon Sep 17 00:00:00 2001 From: Salka Elmadani <935@inference-x.com> Date: Wed, 25 Feb 2026 01:50:55 +0000 Subject: [PATCH] =?UTF-8?q?Echo-IX=20v1.0=20=E2=80=94=20AI=20chat=20powere?= =?UTF-8?q?d=20by=20Inference-X.=20BSL-1.1.=20Signature=20935.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LICENSE | 9 ++ README.md | 21 +++ deploy.sh | 7 + package.json | 9 ++ public/index.html | 389 ++++++++++++++++++++++++++++++++++++++++++++++ server.js | 29 ++++ 6 files changed, 464 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100755 deploy.sh create mode 100644 package.json create mode 100644 public/index.html create mode 100644 server.js diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..17e58f9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +Business Source License 1.1 + +Licensor: Salka Elmadani, Holding SALKA ELMADANI SA (Zug) +Licensed Work: Echo-IX +Change Date: 2030-02-12 +Change License: Apache License 2.0 +Additional Use Grant: Revenue < $1M USD = free use. + +Full text: https://mariadb.com/bsl11/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..7046568 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# Echo-IX + +AI chat interface powered by [Inference-X](https://inference-x.com). 228 KB engine. BSL-1.1. + +## Run + +```bash +IX_HOST=127.0.0.1 IX_PORT=8081 node server.js +``` + +## API + +- `POST /api/echo/chat` — Chat completion +- `POST /api/echo/stream` — SSE streaming +- `GET /api/echo/models` — Available models +- `GET /api/echo/hardware` — Backend status +- `GET /api/health` — Service health + +## License + +BSL-1.1 -> Apache 2.0 on 2030-02-12. Signature 935. diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..00afa76 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -e +echo "=== Echo-IX Deploy ===" +pm2 delete echo-ix 2>/dev/null || true +IX_HOST=${IX_HOST:-127.0.0.1} IX_PORT=${IX_PORT:-8081} pm2 start server.js --name echo-ix +pm2 save +echo "Echo-IX running" diff --git a/package.json b/package.json new file mode 100644 index 0000000..62e7437 --- /dev/null +++ b/package.json @@ -0,0 +1,9 @@ +{ + "name": "echo-ix", + "version": "1.0.0", + "description": "Echo - AI chat by Inference-X", + "main": "server.js", + "scripts": {"start": "node server.js"}, + "license": "BUSL-1.1", + "private": true +} diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..daacf8e --- /dev/null +++ b/public/index.html @@ -0,0 +1,389 @@ + + + + + +Echo - Inference-X + + + + + + + + +
+
+
+
Echov8
+
+ +
+ +
+ ● online + | + + | + - RAM + - vCPU + - loaded +
+
+
+
+
+

Echo

+

228 KB inference engine. Your prompt, your hardware, your data.

+
+ + + +
+
Running on Montagne (64GB/16vCPU). First response may take 10-20s while the model loads. Streaming enabled.
+
+
+
+
+ + +
+ +
+
+
+ + + \ No newline at end of file diff --git a/server.js b/server.js new file mode 100644 index 0000000..2528c51 --- /dev/null +++ b/server.js @@ -0,0 +1,29 @@ +var http=require('http'),fs=require('fs'),path=require('path'); +var PORT=parseInt(process.env.PORT||'3090'); +var IX_HOST=process.env.IX_HOST||'127.0.0.1'; +var IX_PORT=parseInt(process.env.IX_PORT||'8081'); +var DEFAULT_MODEL='qwen2.5-3b-instruct-q4_k_m'; +var rates={}; +function rateOk(k,m,w){var n=Date.now();if(!rates[k])rates[k]=[];rates[k]=rates[k].filter(function(t){return n-t=m)return false;rates[k].push(n);return true} +function ixProxy(p,b,to,cb){var d=JSON.stringify(b);var o={hostname:IX_HOST,port:IX_PORT,path:p,method:'POST',timeout:to,headers:{'Content-Type':'application/json','Content-Length':Buffer.byteLength(d)}};var r=http.request(o,function(res){var c=[];res.on('data',function(x){c.push(x)});res.on('end',function(){try{cb(null,JSON.parse(Buffer.concat(c).toString()))}catch(e){cb(new Error('Parse'))}})});r.on('error',function(e){cb(e)});r.on('timeout',function(){r.destroy();cb(new Error('Timeout'))});r.write(d);r.end()} +function ixGet(p,cb){http.get({hostname:IX_HOST,port:IX_PORT,path:p,timeout:5000},function(res){var c=[];res.on('data',function(x){c.push(x)});res.on('end',function(){try{cb(null,JSON.parse(Buffer.concat(c).toString()))}catch(e){cb(new Error('Parse'))}})}).on('error',function(e){cb(e)})} +function json(res,d,s){res.writeHead(s||200,{'Content-Type':'application/json'});res.end(JSON.stringify(d))} +function readBody(req,cb){var c=[];req.on('data',function(x){c.push(x)});req.on('end',function(){try{cb(JSON.parse(Buffer.concat(c).toString()))}catch(e){cb({})}})} +var srv=http.createServer(function(req,res){ + res.setHeader('Access-Control-Allow-Origin','*'); + res.setHeader('Access-Control-Allow-Methods','GET,POST,OPTIONS'); + res.setHeader('Access-Control-Allow-Headers','Content-Type,Authorization'); + if(req.method==='OPTIONS'){res.writeHead(204);res.end();return} + res.setHeader('X-Frame-Options','DENY'); + res.setHeader('X-Content-Type-Options','nosniff'); + var url=req.url.split('?')[0]; + if(url==='/api/health')return json(res,{status:'ok',service:'echo-ix',version:'1.0',signature:'935'}); + if(url==='/api/echo/models'){ixGet('/v1/models',function(e,d){if(!e&&d&&d.data)return json(res,{data:d.data,source:'live'});json(res,{data:[],source:'offline'})});return} + if(url==='/api/echo/hardware'){ixGet('/v1/models',function(e,d){if(e)return json(res,{status:'offline',ram_gb:64,cores:16,models:0});json(res,{status:'online',ram_gb:64,cores:16,models:d.data?d.data.length:0,active_model:DEFAULT_MODEL})});return} + if(url==='/api/echo/chat'&&req.method==='POST'){var ip=req.socket.remoteAddress;if(!rateOk('e:'+ip,30,6e4))return json(res,{error:'Rate limit'},429);readBody(req,function(b){var msgs=b.messages||[];if(!msgs.length)return json(res,{error:'Messages required'},400);var t0=Date.now();var m=b.model==='auto'||!b.model?DEFAULT_MODEL:b.model;ixProxy('/v1/chat/completions',{model:m,messages:msgs,max_tokens:Math.min(b.max_tokens||512,2048),temperature:b.temperature||0.7},120000,function(e,d){if(e)return json(res,{choices:[{message:{role:'assistant',content:'Backend loading.'},finish_reason:'stop'}],ix:{backend:'offline'}});d.ix={backend:m,latency_ms:Date.now()-t0};json(res,d)})});return} + if(url==='/api/echo/stream'&&req.method==='POST'){var ip=req.socket.remoteAddress;if(!rateOk('e:'+ip,30,6e4))return json(res,{error:'Rate limit'},429);readBody(req,function(b){var msgs=b.messages||[];if(!msgs.length)return json(res,{error:'Messages required'},400);res.writeHead(200,{'Content-Type':'text/event-stream','Cache-Control':'no-cache','Connection':'keep-alive','X-Accel-Buffering':'no'});var m=b.model==='auto'||!b.model?DEFAULT_MODEL:b.model;var d=JSON.stringify({model:m,messages:msgs,max_tokens:Math.min(b.max_tokens||512,2048),temperature:b.temperature||0.7,stream:true});var o={hostname:IX_HOST,port:IX_PORT,path:'/v1/chat/completions',method:'POST',timeout:120000,headers:{'Content-Type':'application/json','Content-Length':Buffer.byteLength(d)}};var p=http.request(o,function(pR){pR.on('data',function(c){res.write(c)});pR.on('end',function(){res.end()})});p.on('error',function(){res.end()});p.write(d);p.end();req.on('close',function(){p.destroy()})});return} + if(url==='/'||url==='/echo')url='/index.html'; + var ext=path.extname(url);var mime={'.html':'text/html','.css':'text/css','.js':'application/javascript','.png':'image/png','.svg':'image/svg+xml'}; + fs.readFile(path.join(__dirname,'public',url),function(e,d){if(e){fs.readFile(path.join(__dirname,'public','index.html'),function(e2,d2){if(e2){res.writeHead(404);res.end('Not found');return}res.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});res.end(d2)});return}res.writeHead(200,{'Content-Type':(mime[ext]||'application/octet-stream')+';charset=utf-8'});res.end(d)}); +}); +srv.listen(PORT,function(){console.log('Echo-IX :'+PORT+' -> '+IX_HOST+':'+IX_PORT+' | sig 935')});