@agent-smith/server 0.0.2

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/dist/main.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { baseRoutes } from "./routes/index.js";
2
+ import type Router from "@koa/router";
3
+ declare function runServer(routes?: ((r: Router) => void)[], staticDir?: string): void;
4
+ export { runServer, baseRoutes, };
package/dist/main.js ADDED
@@ -0,0 +1,7 @@
1
+ import { runserver } from "./server/server.js";
2
+ import { baseRoutes } from "./routes/index.js";
3
+ function runServer(routes, staticDir) {
4
+ const r = routes ? [...baseRoutes, ...routes] : baseRoutes;
5
+ runserver(r, staticDir);
6
+ }
7
+ export { runServer, baseRoutes, };
@@ -0,0 +1,3 @@
1
+ import { Router } from "@koa/router";
2
+ declare const baseRoutes: ((r: Router) => void)[];
3
+ export { baseRoutes };
@@ -0,0 +1,4 @@
1
+ import { getModelsCmd } from "./models.js";
2
+ import { getTaskSettingsCmd, updateTaskSettingsCmd } from "./task_settings.js";
3
+ const baseRoutes = new Array(getModelsCmd, getTaskSettingsCmd, updateTaskSettingsCmd);
4
+ export { baseRoutes };
@@ -0,0 +1,3 @@
1
+ import type Router from '@koa/router';
2
+ declare function getModelsCmd(r: Router): void;
3
+ export { getModelsCmd, };
@@ -0,0 +1,38 @@
1
+ import { backend } from '@agent-smith/cli';
2
+ function getModelsCmd(r) {
3
+ r.get('/models', async (ctx, next) => {
4
+ let mi;
5
+ try {
6
+ mi = await backend.value?.modelsInfo();
7
+ }
8
+ catch (e) {
9
+ ctx.body = "error reading the models";
10
+ ctx.status = 502;
11
+ await next();
12
+ return;
13
+ }
14
+ const ms = {};
15
+ mi?.forEach(m => {
16
+ let prevArg = "";
17
+ let ctx = 0;
18
+ if (backend.value?.providerType == "openai") {
19
+ ms[m.name] = { status: "unknown", ctx: m?.ctx ?? ctx };
20
+ }
21
+ else {
22
+ //@ts-ignore
23
+ m.extra.status.args.forEach(a => {
24
+ if (prevArg == "--ctx-size") {
25
+ ctx = parseInt(a);
26
+ }
27
+ //@ts-ignore
28
+ ms[m.name] = { status: m.extra.status.value, ctx: ctx };
29
+ prevArg = a;
30
+ });
31
+ }
32
+ });
33
+ ctx.body = ms;
34
+ ctx.status = 200;
35
+ await next();
36
+ });
37
+ }
38
+ export { getModelsCmd, };
@@ -0,0 +1,4 @@
1
+ import type Router from '@koa/router';
2
+ declare function getTaskSettingsCmd(r: Router): void;
3
+ declare function updateTaskSettingsCmd(r: Router): void;
4
+ export { getTaskSettingsCmd, updateTaskSettingsCmd, };
@@ -0,0 +1,23 @@
1
+ import { db } from '@agent-smith/cli';
2
+ function getTaskSettingsCmd(r) {
3
+ r.get('/tasks/settings', async (ctx, next) => {
4
+ const ts = db.getTaskSettings();
5
+ ctx.body = ts;
6
+ ctx.status = 200;
7
+ await next();
8
+ });
9
+ }
10
+ function updateTaskSettingsCmd(r) {
11
+ r.post('/tasks/settings/update', async (ctx, next) => {
12
+ const data = ctx.request.body;
13
+ //console.log("DATA", data);
14
+ const name = data.name;
15
+ const settings = data.settings;
16
+ const ts = db.upsertTaskSettings(name, settings);
17
+ db.getTaskSettings(true);
18
+ ctx.body = ts;
19
+ ctx.status = 200;
20
+ await next();
21
+ });
22
+ }
23
+ export { getTaskSettingsCmd, updateTaskSettingsCmd, };
@@ -0,0 +1,3 @@
1
+ import Router from '@koa/router';
2
+ declare const useRouter: (routes?: Array<(r: Router) => void>) => Router<import("koa").DefaultState, import("koa").DefaultContext>;
3
+ export { useRouter };
@@ -0,0 +1,12 @@
1
+ import Router from '@koa/router';
2
+ const useRouter = (routes = []) => {
3
+ const router = new Router();
4
+ routes.forEach((f) => f(router));
5
+ router.get('/ping', async (ctx, next) => {
6
+ ctx.body = { ok: true };
7
+ ctx.status = 200;
8
+ await next();
9
+ });
10
+ return router;
11
+ };
12
+ export { useRouter };
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { Router } from "@koa/router";
3
+ declare function runserver(routes?: ((r: Router) => void)[], staticDir?: string): void;
4
+ export { runserver };
@@ -0,0 +1,301 @@
1
+ #!/usr/bin/env node
2
+ import { default as color } from "ansi-colors";
3
+ import Koa from 'koa';
4
+ import bodyParser from "koa-bodyparser";
5
+ import cors from '@koa/cors';
6
+ import { useRouter } from './router.js';
7
+ import websockify from 'koa-websocket';
8
+ import route from 'koa-route';
9
+ import serve from "koa-static";
10
+ import { executeTask, executeWorkflow, init } from '@agent-smith/cli';
11
+ /* import { argv } from 'process';
12
+
13
+ let env = "production";
14
+
15
+ if (argv.length > 2) {
16
+ for (const arg of argv.slice(2, argv.length)) {
17
+ if (arg == "-d") {
18
+ env = "dev"
19
+ }
20
+ }
21
+ }*/
22
+ const logger = async (ctx, next) => {
23
+ const start = Date.now();
24
+ await next();
25
+ const duration = Date.now() - start;
26
+ console.log(`${ctx.method} ${ctx.url} - ${ctx.status} - ${duration}ms`);
27
+ };
28
+ const app = websockify(new Koa());
29
+ app.use(bodyParser());
30
+ app.use(logger);
31
+ app.use(cors({
32
+ credentials: true
33
+ }));
34
+ app.ws.use(function (ctx, next) {
35
+ return next();
36
+ });
37
+ function createManualPromise() {
38
+ let resolveFn;
39
+ let rejectFn;
40
+ const promise = new Promise((resolve, reject) => {
41
+ resolveFn = resolve;
42
+ rejectFn = reject;
43
+ });
44
+ return {
45
+ promise,
46
+ resolve: resolveFn,
47
+ reject: rejectFn
48
+ };
49
+ }
50
+ function runserver(routes, staticDir) {
51
+ if (staticDir) {
52
+ app.use(serve(staticDir));
53
+ }
54
+ const router = useRouter(routes);
55
+ const sendTokensInterval = 100;
56
+ const confirmToolCalls = {};
57
+ app.ws.use(route.all('/ws', function (ctx) {
58
+ //ctx.websocket.send('Hello World');
59
+ let abort = new AbortController();
60
+ ctx.websocket.on('message', async function (message) {
61
+ const msg = JSON.parse(message);
62
+ if (!msg?.options) {
63
+ msg.options = {};
64
+ }
65
+ //console.log("ABO", abort);
66
+ msg.options.abort = abort;
67
+ //console.log(msg)
68
+ if (msg.type == "system") {
69
+ if (msg.command == "stop") {
70
+ //console.log("STOP CMD");
71
+ abort.abort("stopped");
72
+ abort = new AbortController();
73
+ return;
74
+ }
75
+ else if (msg.command == "confirmtool") {
76
+ if (!(msg.payload.id in confirmToolCalls)) {
77
+ const rsm = {
78
+ type: "error",
79
+ msg: `can not confirm tool call: unknown tool id ${msg.payload.id}`,
80
+ };
81
+ ctx.websocket.send(JSON.stringify(rsm));
82
+ return;
83
+ }
84
+ if (msg.payload.confirm == true) {
85
+ confirmToolCalls[msg.payload.id](true);
86
+ }
87
+ else {
88
+ confirmToolCalls[msg.payload.id](false);
89
+ }
90
+ delete confirmToolCalls[msg.payload.id];
91
+ }
92
+ else {
93
+ console.error(`unknown system command ${msg.payload}`);
94
+ }
95
+ }
96
+ else {
97
+ if (!msg?.payload) {
98
+ const rsm = {
99
+ type: "error",
100
+ msg: "provide a payload",
101
+ };
102
+ ctx.websocket.send(JSON.stringify(rsm));
103
+ return;
104
+ }
105
+ await init();
106
+ if (msg.feature == "task") {
107
+ let buf = "";
108
+ msg.options.onToken = (t) => {
109
+ buf += t;
110
+ process.stdout.write(t);
111
+ };
112
+ try {
113
+ const it = setInterval(() => {
114
+ const rsm = {
115
+ type: "token",
116
+ msg: buf,
117
+ };
118
+ ctx.websocket.send(JSON.stringify(rsm));
119
+ buf = "";
120
+ }, sendTokensInterval);
121
+ const res = await executeTask(msg.command, msg.payload, msg.options);
122
+ setTimeout(() => {
123
+ clearInterval(it);
124
+ }, sendTokensInterval);
125
+ //console.dir(res, { depth: 3 });
126
+ let r;
127
+ if (res.template.id == "none") {
128
+ r = { assistant: res.answer.text };
129
+ }
130
+ else {
131
+ r = res.template.history.pop();
132
+ }
133
+ const rsm = {
134
+ type: "finalresult",
135
+ msg: JSON.stringify(r),
136
+ };
137
+ ctx.websocket.send(JSON.stringify(rsm));
138
+ }
139
+ catch (e) {
140
+ const rsm = {
141
+ type: "error",
142
+ msg: `${e}`,
143
+ };
144
+ ctx.websocket.send(JSON.stringify(rsm));
145
+ }
146
+ }
147
+ else if (msg.feature == "agent") {
148
+ msg.options.onToolsTurnStart = (tcs) => {
149
+ const rsm = {
150
+ type: "finalresult",
151
+ msg: JSON.stringify(tcs),
152
+ };
153
+ ctx.websocket.send(JSON.stringify(rsm));
154
+ };
155
+ msg.options.onToolsTurnEnd = (tr) => {
156
+ const rsm = {
157
+ type: "toolsturnend",
158
+ msg: JSON.stringify(tr),
159
+ };
160
+ ctx.websocket.send(JSON.stringify(rsm));
161
+ };
162
+ msg.options.onTurnEnd = (ht) => {
163
+ const rsm = {
164
+ type: "turnend",
165
+ msg: JSON.stringify(ht),
166
+ };
167
+ ctx.websocket.send(JSON.stringify(rsm));
168
+ };
169
+ msg.options.onAssistant = (txt) => {
170
+ const rsm = {
171
+ type: "assistant",
172
+ msg: txt,
173
+ };
174
+ ctx.websocket.send(JSON.stringify(rsm));
175
+ };
176
+ msg.options.onToolCall = (tc) => {
177
+ if (!tc?.id) {
178
+ tc.id = crypto.randomUUID();
179
+ }
180
+ const rsm = {
181
+ type: "toolcall",
182
+ msg: JSON.stringify(tc),
183
+ };
184
+ ctx.websocket.send(JSON.stringify(rsm));
185
+ console.log("\n⚒️ ", color.bold(msg.command), "=>", `${color.yellowBright(tc.name)}`, tc.arguments);
186
+ };
187
+ msg.options.onToolCallEnd = (id, tr) => {
188
+ let toolResData;
189
+ if (typeof tr == 'object') {
190
+ if (tr?.answer?.text) {
191
+ // comes from an inference task
192
+ toolResData = tr.answer.text;
193
+ }
194
+ else {
195
+ toolResData = JSON.stringify(tr);
196
+ }
197
+ }
198
+ else {
199
+ toolResData = tr.toString();
200
+ }
201
+ const rsm = {
202
+ type: "toolcallend",
203
+ msg: `${id}<|xtool_call_id|>` + toolResData,
204
+ };
205
+ ctx.websocket.send(JSON.stringify(rsm));
206
+ };
207
+ msg.options.confirmToolUsage = async (tc) => {
208
+ if (!tc?.id) {
209
+ tc.id = crypto.randomUUID();
210
+ }
211
+ const rsm = {
212
+ type: "toolcallconfirm",
213
+ msg: JSON.stringify(tc),
214
+ };
215
+ const { promise, resolve } = createManualPromise();
216
+ confirmToolCalls[tc.id] = resolve;
217
+ ctx.websocket.send(JSON.stringify(rsm));
218
+ const res = await promise;
219
+ return res;
220
+ };
221
+ msg.options.isAgent = true;
222
+ try {
223
+ let buf = "";
224
+ msg.options.onToken = (t) => {
225
+ buf += t;
226
+ process.stdout.write(t);
227
+ };
228
+ const it = setInterval(() => {
229
+ if (buf == "") {
230
+ return;
231
+ }
232
+ ;
233
+ const rsm = {
234
+ type: "token",
235
+ msg: buf,
236
+ };
237
+ ctx.websocket.send(JSON.stringify(rsm));
238
+ buf = "";
239
+ }, sendTokensInterval);
240
+ const res = await executeTask(msg.command, msg.payload, msg.options);
241
+ setTimeout(() => {
242
+ clearInterval(it);
243
+ }, sendTokensInterval);
244
+ const ht = JSON.stringify(res.template.history.pop());
245
+ //console.log("FINAL MSG", ht)
246
+ const rsm = {
247
+ type: "finalresult",
248
+ msg: ht,
249
+ };
250
+ ctx.websocket.send(JSON.stringify(rsm));
251
+ }
252
+ catch (e) {
253
+ const rsm = {
254
+ type: "error",
255
+ msg: `${e}`
256
+ };
257
+ ctx.websocket.send(JSON.stringify(rsm));
258
+ }
259
+ }
260
+ else if (msg.feature == "workflow") {
261
+ try {
262
+ const res = await executeWorkflow(msg.command, msg.payload, msg.options);
263
+ const rsm = {
264
+ type: "finalresult",
265
+ msg: res,
266
+ };
267
+ ctx.websocket.send(JSON.stringify(rsm));
268
+ }
269
+ catch (e) {
270
+ const rsm = {
271
+ type: "error",
272
+ msg: `${e}`
273
+ };
274
+ ctx.websocket.send(JSON.stringify(rsm));
275
+ }
276
+ }
277
+ /*else if (msg.type == "cmd") {
278
+ await init();
279
+ msg.options.onToken = (t: string) => {
280
+ process.stdout.write(t);
281
+ ctx.websocket.send(t);
282
+ };
283
+ await executeCmd(msg.name, msg.payload, msg.options);
284
+ }*/
285
+ else {
286
+ const rsm = {
287
+ type: "error",
288
+ msg: "command type " + msg.feature + " not supported"
289
+ };
290
+ ctx.websocket.send(JSON.stringify(rsm));
291
+ }
292
+ }
293
+ abort = new AbortController();
294
+ });
295
+ }));
296
+ app.use(router.routes()).use(router.allowedMethods());
297
+ app.listen(5184, () => {
298
+ console.log('Please open localhost:5184 in a browser');
299
+ });
300
+ }
301
+ export { runserver };
@@ -0,0 +1,2 @@
1
+ declare const dirpath: string;
2
+ export { dirpath };
@@ -0,0 +1,5 @@
1
+ import { fileURLToPath } from 'url';
2
+ import path from 'path';
3
+ const __filename = fileURLToPath(import.meta.url);
4
+ const dirpath = path.dirname(__filename);
5
+ export { dirpath };
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@agent-smith/server",
3
+ "version": "0.0.2",
4
+ "description": "Agent Smith Nodejs server",
5
+ "scripts": {
6
+ "test": "echo \"Error: no test specified\" && exit 1",
7
+ "build": "rm -rf dist && tsc",
8
+ "start": "node dist/index.js",
9
+ "server": "node --loader ts-node/esm src/index.ts",
10
+ "devserver": "node --loader ts-node/esm --watch src/index.ts"
11
+ },
12
+ "type": "module",
13
+ "dependencies": {
14
+ "@agent-smith/cli": "^0.0.111",
15
+ "@koa/cors": "^5.0.0",
16
+ "@koa/router": "^15.3.0",
17
+ "ansi-colors": "^4.1.3",
18
+ "koa": "^3.1.1",
19
+ "koa-bodyparser": "^4.4.1",
20
+ "koa-route": "^4.0.1",
21
+ "koa-static": "^5.0.0",
22
+ "koa-websocket": "^7.0.0"
23
+ },
24
+ "devDependencies": {
25
+ "@agent-smith/types": "^0.0.2",
26
+ "@locallm/types": "^0.7.0",
27
+ "@types/better-sqlite3": "^7.6.13",
28
+ "@types/koa": "^3.0.1",
29
+ "@types/koa__cors": "^5.0.1",
30
+ "@types/koa-bodyparser": "^4.3.13",
31
+ "@types/koa-route": "^3.2.9",
32
+ "@types/koa-static": "^4.0.4",
33
+ "@types/koa-websocket": "^5.0.11",
34
+ "@types/node": "^25.3.0",
35
+ "ts-node": "^10.9.2",
36
+ "tslib": "2.8.1",
37
+ "typescript": "^5.9.3"
38
+ },
39
+ "preferGlobal": true,
40
+ "main": "./dist/main.js",
41
+ "types": "./dist/main.d.ts",
42
+ "exports": {
43
+ ".": {
44
+ "import": "./dist/main.js"
45
+ }
46
+ },
47
+ "files": [
48
+ "dist"
49
+ ],
50
+ "bin": {
51
+ "agentserver": "./dist/index.js"
52
+ },
53
+ "publishConfig": {
54
+ "access": "public",
55
+ "registry": "https://registry.npmjs.org/"
56
+ },
57
+ "license": "MIT"
58
+ }