@takimcizgisi/ajan 0.2.5

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.
Files changed (79) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +366 -0
  3. package/config/models.json +54 -0
  4. package/dist/cli/index.d.ts +2 -0
  5. package/dist/cli/index.js +741 -0
  6. package/dist/cli/tui.d.ts +145 -0
  7. package/dist/cli/tui.js +1003 -0
  8. package/dist/config/configManager.d.ts +18 -0
  9. package/dist/config/configManager.js +204 -0
  10. package/dist/config/types.d.ts +98 -0
  11. package/dist/config/types.js +2 -0
  12. package/dist/core/agent.d.ts +88 -0
  13. package/dist/core/agent.js +222 -0
  14. package/dist/electron/main.d.ts +1 -0
  15. package/dist/electron/main.js +364 -0
  16. package/dist/electron/preload.d.ts +1 -0
  17. package/dist/electron/preload.js +67 -0
  18. package/dist/engine/llama.d.ts +39 -0
  19. package/dist/engine/llama.js +146 -0
  20. package/dist/engine/modelManager.d.ts +18 -0
  21. package/dist/engine/modelManager.js +73 -0
  22. package/dist/index.d.ts +9 -0
  23. package/dist/index.js +9 -0
  24. package/dist/service.d.ts +90 -0
  25. package/dist/service.js +268 -0
  26. package/dist/tools/data.d.ts +3 -0
  27. package/dist/tools/data.js +111 -0
  28. package/dist/tools/edit.d.ts +2 -0
  29. package/dist/tools/edit.js +48 -0
  30. package/dist/tools/file.d.ts +6 -0
  31. package/dist/tools/file.js +274 -0
  32. package/dist/tools/filesys.d.ts +3 -0
  33. package/dist/tools/filesys.js +74 -0
  34. package/dist/tools/memory.d.ts +3 -0
  35. package/dist/tools/memory.js +69 -0
  36. package/dist/tools/patch.d.ts +15 -0
  37. package/dist/tools/patch.js +129 -0
  38. package/dist/tools/registry.d.ts +10 -0
  39. package/dist/tools/registry.js +64 -0
  40. package/dist/tools/taskComplete.d.ts +2 -0
  41. package/dist/tools/taskComplete.js +19 -0
  42. package/dist/tools/terminal.d.ts +2 -0
  43. package/dist/tools/terminal.js +130 -0
  44. package/dist/tools/utils.d.ts +7 -0
  45. package/dist/tools/utils.js +63 -0
  46. package/dist/tools/web.d.ts +8 -0
  47. package/dist/tools/web.js +181 -0
  48. package/dist/utils/clipboard.d.ts +2 -0
  49. package/dist/utils/clipboard.js +40 -0
  50. package/dist/utils/lock.d.ts +12 -0
  51. package/dist/utils/lock.js +68 -0
  52. package/dist/utils/paths.d.ts +13 -0
  53. package/dist/utils/paths.js +58 -0
  54. package/dist/utils/projectContext.d.ts +2 -0
  55. package/dist/utils/projectContext.js +24 -0
  56. package/dist/utils/sacGuard.d.ts +4 -0
  57. package/dist/utils/sacGuard.js +47 -0
  58. package/dist/utils/sessions.d.ts +29 -0
  59. package/dist/utils/sessions.js +64 -0
  60. package/dist/utils/spinner.d.ts +10 -0
  61. package/dist/utils/spinner.js +32 -0
  62. package/package.json +106 -0
  63. package/src/gui/assets/image/AJAN_LOGO.svg +1 -0
  64. package/src/gui/assets/image/ajan.png +0 -0
  65. package/src/gui/assets/image/logo.svg +1 -0
  66. package/src/gui/assets/image/opencode.svg +18 -0
  67. package/src/gui/css/style.css +430 -0
  68. package/src/gui/html/chat.html +34 -0
  69. package/src/gui/html/downloads.html +11 -0
  70. package/src/gui/html/hakkinda.html +26 -0
  71. package/src/gui/html/index.html +82 -0
  72. package/src/gui/html/projects.html +12 -0
  73. package/src/gui/html/settings.html +4 -0
  74. package/src/gui/js/app.js +197 -0
  75. package/src/gui/js/chat.js +434 -0
  76. package/src/gui/js/downloads.js +172 -0
  77. package/src/gui/js/hakkinda.js +16 -0
  78. package/src/gui/js/projects.js +113 -0
  79. package/src/gui/js/settings.js +177 -0
@@ -0,0 +1,364 @@
1
+ import { app, BrowserWindow, ipcMain, shell } from "electron";
2
+ import { join, dirname } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { readFileSync, existsSync, mkdirSync } from "node:fs";
5
+ import { execSync } from "node:child_process";
6
+ import { homedir } from "node:os";
7
+ import { AjanService } from "../service.js";
8
+ import { getDataDir } from "../utils/paths.js";
9
+ import { acquireLock, releaseLock } from "../utils/lock.js";
10
+ const isDev = process.argv.includes("--dev");
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = dirname(__filename);
13
+ let mainWindow = null;
14
+ let service = null;
15
+ let isMaximized = false;
16
+ function getVersion() {
17
+ try {
18
+ const pkg = JSON.parse(readFileSync(join(__dirname, "..", "..", "package.json"), "utf8"));
19
+ return pkg.version ?? "0.0.0";
20
+ }
21
+ catch {
22
+ return "0.0.0";
23
+ }
24
+ }
25
+ function getGuiPath() {
26
+ return join(__dirname, "..", "..", "src", "gui");
27
+ }
28
+ function createService() {
29
+ if (service)
30
+ return;
31
+ service = new AjanService({
32
+ onTextChunk: (text) => mainWindow?.webContents.send("agent:text-chunk", text),
33
+ onThinkingChunk: (text) => mainWindow?.webContents.send("agent:thinking-chunk", text),
34
+ onToolCall: (name, params) => mainWindow?.webContents.send("agent:tool-call", name, params),
35
+ onToolResult: (name, ok, output) => mainWindow?.webContents.send("agent:tool-result", name, ok, output),
36
+ onStepStart: (step, maxSteps) => mainWindow?.webContents.send("agent:step-start", step, maxSteps),
37
+ onComplete: (response) => mainWindow?.webContents.send("agent:complete", response),
38
+ onError: (error) => mainWindow?.webContents.send("agent:error", error.message),
39
+ onContextTrimmed: () => { },
40
+ onModelLoadProgress: (pct) => mainWindow?.webContents.send("agent:load-progress", pct),
41
+ onModelLoadComplete: () => mainWindow?.webContents.send("agent:load-complete")
42
+ });
43
+ }
44
+ function createWindow() {
45
+ mainWindow = new BrowserWindow({
46
+ width: 1200,
47
+ height: 800,
48
+ minWidth: 800,
49
+ minHeight: 600,
50
+ frame: false,
51
+ titleBarStyle: "hidden",
52
+ backgroundColor: "#202020",
53
+ webPreferences: {
54
+ preload: join(__dirname, "preload.js"),
55
+ contextIsolation: true,
56
+ nodeIntegration: false,
57
+ sandbox: false
58
+ },
59
+ icon: join(getGuiPath(), "assets", "image", "ajan.png"),
60
+ title: `AJAN AI v${getVersion()}`
61
+ });
62
+ mainWindow.loadFile(join(getGuiPath(), "html", "index.html"));
63
+ if (isDev) {
64
+ mainWindow.webContents.openDevTools({ mode: "detach" });
65
+ }
66
+ mainWindow.on("closed", () => {
67
+ mainWindow = null;
68
+ });
69
+ mainWindow.on("maximize", () => {
70
+ isMaximized = true;
71
+ mainWindow?.webContents.send("window:maximized-changed", true);
72
+ });
73
+ mainWindow.on("unmaximize", () => {
74
+ isMaximized = false;
75
+ mainWindow?.webContents.send("window:maximized-changed", false);
76
+ });
77
+ mainWindow.webContents.on("did-finish-load", () => {
78
+ if (mainWindow?.isMaximized()) {
79
+ isMaximized = true;
80
+ mainWindow.webContents.send("window:maximized-changed", true);
81
+ }
82
+ restoreLastSession();
83
+ });
84
+ mainWindow.webContents.on("console-message", (_event, level, message, line, sourceId) => {
85
+ const line2 = `[renderer:${level}] ${sourceId}:${line} ${message}\n`;
86
+ console.log(line2.trimEnd());
87
+ try {
88
+ const fsmod = require("node:fs");
89
+ fsmod.appendFileSync(require("node:path").join(require("node:os").homedir(), ".ajan", "renderer.log"), line2);
90
+ }
91
+ catch { /* empty */ }
92
+ });
93
+ mainWindow.webContents.on("preload-error", (_event, preloadPath, error) => {
94
+ console.log(`[preload-error] ${preloadPath}: ${error.message}`);
95
+ });
96
+ mainWindow.webContents.setWindowOpenHandler(({ url }) => {
97
+ shell.openExternal(url);
98
+ return { action: "deny" };
99
+ });
100
+ }
101
+ async function restoreLastSession() {
102
+ try {
103
+ if (!service)
104
+ return;
105
+ const sessions = await service.listSessions();
106
+ const last = sessions[0];
107
+ if (!last)
108
+ return;
109
+ mainWindow?.webContents.send("agent:session-restore", {
110
+ id: last.id,
111
+ title: last.title,
112
+ messages: last.messages,
113
+ createdAt: last.createdAt,
114
+ updatedAt: last.updatedAt
115
+ });
116
+ }
117
+ catch { /* empty */ }
118
+ }
119
+ function setupIpc() {
120
+ // Agent: send
121
+ ipcMain.handle("agent:send", async (_event, message, modelId) => {
122
+ if (!service)
123
+ return { ok: false, error: "Servis başlatılamadı" };
124
+ return await service.sendMessage(message, modelId || undefined);
125
+ });
126
+ // Agent: abort
127
+ ipcMain.handle("agent:abort", () => {
128
+ service?.abort();
129
+ return { ok: true };
130
+ });
131
+ // Agent: reset
132
+ ipcMain.handle("agent:reset", () => {
133
+ service?.reset();
134
+ return { ok: true };
135
+ });
136
+ // Agent: status
137
+ ipcMain.handle("agent:status", () => {
138
+ return { active: service?.isActive ?? false, contextStats: service?.getContextStats() ?? null };
139
+ });
140
+ // Config
141
+ ipcMain.handle("app:version", () => getVersion());
142
+ ipcMain.handle("config:get", () => {
143
+ return service?.getConfig() ?? {};
144
+ });
145
+ ipcMain.handle("config:set", async (_event, newConfig) => {
146
+ if (!service)
147
+ return { ok: false, error: "Servis başlatılamadı" };
148
+ await service.setConfig(newConfig);
149
+ return { ok: true };
150
+ });
151
+ // Model
152
+ ipcMain.handle("model:info", () => {
153
+ return service?.getModelInfo() ?? { modelId: "", name: "Bilinmeyen", installed: false };
154
+ });
155
+ ipcMain.handle("model:list", () => {
156
+ return service?.listModels() ?? [];
157
+ });
158
+ ipcMain.handle("model:install", async (_event, id) => {
159
+ if (!service)
160
+ return { ok: false, error: "Servis başlatılamadı" };
161
+ return await service.installModel(id);
162
+ });
163
+ ipcMain.handle("model:remove", async (_event, id) => {
164
+ if (!service)
165
+ return { ok: false, error: "Servis başlatılamadı" };
166
+ return await service.removeModel(id);
167
+ });
168
+ ipcMain.handle("model:use", async (_event, id) => {
169
+ if (!service)
170
+ return { ok: false, error: "Servis başlatılamadı" };
171
+ return await service.useModel(id);
172
+ });
173
+ ipcMain.handle("model:refresh", async () => {
174
+ if (!service)
175
+ return { ok: false, error: "Servis başlatılamadı" };
176
+ return await service.refreshModels();
177
+ });
178
+ // Project
179
+ ipcMain.handle("project:list", () => {
180
+ return service?.listProjects() ?? [];
181
+ });
182
+ ipcMain.handle("project:add", async (_event, path) => {
183
+ if (!service)
184
+ return { ok: false, error: "Servis başlatılamadı" };
185
+ return await service.addProject(path);
186
+ });
187
+ ipcMain.handle("project:remove", async (_event, path) => {
188
+ if (!service)
189
+ return { ok: false, error: "Servis başlatılamadı" };
190
+ return await service.removeProject(path);
191
+ });
192
+ ipcMain.handle("project:set-active", async (_event, path) => {
193
+ if (!service)
194
+ return { ok: false, error: "Servis başlatılamadı" };
195
+ return await service.setActiveProject(path);
196
+ });
197
+ ipcMain.handle("project:pick-dir", async () => {
198
+ const { dialog } = await import("electron");
199
+ const result = await dialog.showOpenDialog(mainWindow, { properties: ["openDirectory"] });
200
+ return result.canceled ? null : (result.filePaths[0] ?? null);
201
+ });
202
+ ipcMain.handle("project:exists", async (_event, path) => {
203
+ try {
204
+ const { statSync } = await import("node:fs");
205
+ return statSync(path).isDirectory();
206
+ }
207
+ catch {
208
+ return false;
209
+ }
210
+ });
211
+ // Session
212
+ ipcMain.handle("session:list", async () => {
213
+ return service?.listSessions() ?? [];
214
+ });
215
+ ipcMain.handle("session:load", async (_event, id) => {
216
+ if (!service)
217
+ return { ok: false, error: "Servis başlatılamadı" };
218
+ const data = await service.loadSession(id);
219
+ if (!data)
220
+ return { ok: false, error: "Oturum bulunamadı" };
221
+ return { ok: true, data };
222
+ });
223
+ ipcMain.handle("session:save", async (_event, session) => {
224
+ if (!service)
225
+ return { ok: false, error: "Servis başlatılamadı" };
226
+ await service.saveSession(session);
227
+ return { ok: true };
228
+ });
229
+ ipcMain.handle("session:new", () => {
230
+ service?.reset();
231
+ return { ok: true };
232
+ });
233
+ ipcMain.handle("session:generate-id", () => {
234
+ return service?.generateSessionId() ?? String(Date.now());
235
+ });
236
+ ipcMain.handle("session:get-history", () => {
237
+ return service?.getChatHistory() ?? [];
238
+ });
239
+ // System
240
+ ipcMain.handle("system:gpu", async () => {
241
+ try {
242
+ const gpus = await service?.detectGpu() ?? [];
243
+ return { gpus };
244
+ }
245
+ catch {
246
+ return { gpus: [] };
247
+ }
248
+ });
249
+ ipcMain.handle("system:cwd", () => {
250
+ return { cwd: process.cwd() };
251
+ });
252
+ ipcMain.handle("system:notes", async () => {
253
+ const { readFileSync: readFs } = await import("node:fs");
254
+ const notesPath = join(getDataDir(), "notes.json");
255
+ try {
256
+ return JSON.parse(readFs(notesPath, "utf8"));
257
+ }
258
+ catch {
259
+ return [];
260
+ }
261
+ });
262
+ ipcMain.handle("system:notes:add", async (_event, text) => {
263
+ const { readFileSync: readFs, writeFileSync: writeFs } = await import("node:fs");
264
+ const notesPath = join(getDataDir(), "notes.json");
265
+ let notes = [];
266
+ try {
267
+ notes = JSON.parse(readFs(notesPath, "utf8"));
268
+ }
269
+ catch { /* empty */ }
270
+ notes.push({ text, createdAt: new Date().toISOString() });
271
+ writeFs(notesPath, JSON.stringify(notes, null, 2), "utf8");
272
+ return { ok: true };
273
+ });
274
+ // Window controls
275
+ ipcMain.handle("window:minimize", () => mainWindow?.minimize());
276
+ ipcMain.handle("window:maximize", () => {
277
+ if (mainWindow?.isMaximized())
278
+ mainWindow.unmaximize();
279
+ else
280
+ mainWindow?.maximize();
281
+ });
282
+ ipcMain.handle("window:close", () => mainWindow?.close());
283
+ ipcMain.handle("window:is-maximized", () => isMaximized);
284
+ // GUI file reader
285
+ ipcMain.handle("gui:read-file", (_event, relativePath) => {
286
+ try {
287
+ const guiPath = getGuiPath();
288
+ const filePath = join(guiPath, relativePath);
289
+ if (!filePath.startsWith(guiPath)) {
290
+ return { ok: false, error: "Yasak yol" };
291
+ }
292
+ const content = readFileSync(filePath, "utf8");
293
+ return { ok: true, content };
294
+ }
295
+ catch (err) {
296
+ return { ok: false, error: err.message };
297
+ }
298
+ });
299
+ // Windows shortcut
300
+ ipcMain.handle("gui:create-shortcut", () => {
301
+ try {
302
+ const projRoot = join(__dirname, "..", "..");
303
+ const electronExe = join(projRoot, "node_modules", "electron", "dist", "electron.exe");
304
+ const targetJs = join(projRoot, "dist", "electron", "main.js");
305
+ if (!existsSync(electronExe)) {
306
+ return { ok: false, error: "electron.exe bulunamadı" };
307
+ }
308
+ const desktopPath = join(homedir(), "Desktop");
309
+ const startMenuPath = join(homedir(), "AppData", "Roaming", "Microsoft", "Windows", "Start Menu", "Programs");
310
+ const makeShortcut = (dir, name) => {
311
+ const lnkPath = join(dir, `${name}.lnk`);
312
+ const ps = [
313
+ `$ws = New-Object -ComObject WScript.Shell`,
314
+ `$s = $ws.CreateShortcut('${lnkPath.replace(/'/g, "''")}')`,
315
+ `$s.TargetPath = '${electronExe.replace(/'/g, "''")}'`,
316
+ `$s.Arguments = '${targetJs.replace(/'/g, "''")}'`,
317
+ `$s.WorkingDirectory = '${projRoot.replace(/'/g, "''")}'`,
318
+ `$s.Description = 'AJAN AI Yerel Asistan'`,
319
+ `$s.Save()`
320
+ ].join("; ");
321
+ execSync(`powershell -NoProfile -Command "${ps}"`, { stdio: "pipe" });
322
+ };
323
+ makeShortcut(desktopPath, "AJAN AI");
324
+ const ajanStartDir = join(startMenuPath, "AJAN AI");
325
+ if (!existsSync(ajanStartDir)) {
326
+ mkdirSync(ajanStartDir, { recursive: true });
327
+ }
328
+ makeShortcut(ajanStartDir, "AJAN AI");
329
+ return { ok: true };
330
+ }
331
+ catch (err) {
332
+ return { ok: false, error: err.message };
333
+ }
334
+ });
335
+ }
336
+ // ── App Lifecycle ──
337
+ app.whenReady().then(() => {
338
+ const lock = acquireLock("gui");
339
+ if (!lock.ok) {
340
+ console.log(lock.error);
341
+ app.quit();
342
+ return;
343
+ }
344
+ createService();
345
+ setupIpc();
346
+ createWindow();
347
+ app.on("activate", () => {
348
+ if (BrowserWindow.getAllWindows().length === 0)
349
+ createWindow();
350
+ });
351
+ });
352
+ app.on("window-all-closed", async () => {
353
+ await service?.dispose();
354
+ service = null;
355
+ releaseLock();
356
+ if (process.platform !== "darwin")
357
+ app.quit();
358
+ });
359
+ app.on("before-quit", async () => {
360
+ await service?.dispose();
361
+ service = null;
362
+ releaseLock();
363
+ });
364
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,67 @@
1
+ import { contextBridge, ipcRenderer } from "electron";
2
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3
+ function onIpc(channel, cb) {
4
+ const handler = (_event, ...args) => cb(...args);
5
+ ipcRenderer.on(channel, handler);
6
+ return () => { ipcRenderer.removeListener(channel, handler); };
7
+ }
8
+ contextBridge.exposeInMainWorld("ajan", {
9
+ // ── Agent ──
10
+ sendMessage: (message, model) => ipcRenderer.invoke("agent:send", message, model),
11
+ abort: () => ipcRenderer.invoke("agent:abort"),
12
+ reset: () => ipcRenderer.invoke("agent:reset"),
13
+ getStatus: () => ipcRenderer.invoke("agent:status"),
14
+ // ── Agent Events ──
15
+ onTextChunk: (cb) => onIpc("agent:text-chunk", cb),
16
+ onThinkingChunk: (cb) => onIpc("agent:thinking-chunk", cb),
17
+ onToolCall: (cb) => onIpc("agent:tool-call", cb),
18
+ onToolResult: (cb) => onIpc("agent:tool-result", cb),
19
+ onStepStart: (cb) => onIpc("agent:step-start", cb),
20
+ onComplete: (cb) => onIpc("agent:complete", cb),
21
+ onError: (cb) => onIpc("agent:error", cb),
22
+ onLoadProgress: (cb) => onIpc("agent:load-progress", cb),
23
+ onLoadComplete: (cb) => onIpc("agent:load-complete", cb),
24
+ // ── Config ──
25
+ getConfig: () => ipcRenderer.invoke("config:get"),
26
+ setConfig: (config) => ipcRenderer.invoke("config:set", config),
27
+ // ── Model ──
28
+ getModelInfo: () => ipcRenderer.invoke("model:info"),
29
+ listModels: () => ipcRenderer.invoke("model:list"),
30
+ installModel: (id) => ipcRenderer.invoke("model:install", id),
31
+ removeModel: (id) => ipcRenderer.invoke("model:remove", id),
32
+ useModel: (id) => ipcRenderer.invoke("model:use", id),
33
+ refreshModels: () => ipcRenderer.invoke("model:refresh"),
34
+ // ── Project ──
35
+ listProjects: () => ipcRenderer.invoke("project:list"),
36
+ addProject: (path) => ipcRenderer.invoke("project:add", path),
37
+ removeProject: (path) => ipcRenderer.invoke("project:remove", path),
38
+ setActiveProject: (path) => ipcRenderer.invoke("project:set-active", path),
39
+ pickDir: () => ipcRenderer.invoke("project:pick-dir"),
40
+ projectExists: (path) => ipcRenderer.invoke("project:exists", path),
41
+ // ── App ──
42
+ getVersion: () => ipcRenderer.invoke("app:version"),
43
+ // ── Session ──
44
+ listSessions: () => ipcRenderer.invoke("session:list"),
45
+ loadSession: (id) => ipcRenderer.invoke("session:load", id),
46
+ saveSession: (session) => ipcRenderer.invoke("session:save", session),
47
+ newSession: () => ipcRenderer.invoke("session:new"),
48
+ generateSessionId: () => ipcRenderer.invoke("session:generate-id"),
49
+ getChatHistory: () => ipcRenderer.invoke("session:get-history"),
50
+ onSessionRestore: (cb) => onIpc("agent:session-restore", cb),
51
+ // ── System ──
52
+ getGpuInfo: () => ipcRenderer.invoke("system:gpu"),
53
+ getCwd: () => ipcRenderer.invoke("system:cwd"),
54
+ getNotes: () => ipcRenderer.invoke("system:notes"),
55
+ addNote: (text) => ipcRenderer.invoke("system:notes:add", text),
56
+ // ── GUI Files ──
57
+ readGuiFile: (relativePath) => ipcRenderer.invoke("gui:read-file", relativePath),
58
+ createShortcut: () => ipcRenderer.invoke("gui:create-shortcut"),
59
+ // ── Window Events ──
60
+ onMaximized: (cb) => onIpc("window:maximized-changed", cb),
61
+ isMaximized: () => ipcRenderer.invoke("window:is-maximized"),
62
+ // ── Window Controls ──
63
+ minimize: () => ipcRenderer.invoke("window:minimize"),
64
+ maximize: () => ipcRenderer.invoke("window:maximize"),
65
+ close: () => ipcRenderer.invoke("window:close")
66
+ });
67
+ //# sourceMappingURL=preload.js.map
@@ -0,0 +1,39 @@
1
+ import { LlamaChatSession, type ChatWrapper, type ChatSessionModelFunctions } from "node-llama-cpp";
2
+ import type { AjanConfig, ChatWrapperName } from "../config/types.js";
3
+ export type LlamaEngineOptions = {
4
+ modelPath: string;
5
+ config: AjanConfig;
6
+ chatWrapperName?: ChatWrapperName;
7
+ gpuBackend?: AjanConfig["gpu"];
8
+ onProgress?: (progress: number) => void;
9
+ signal?: AbortSignal;
10
+ };
11
+ export declare class LlamaEngine {
12
+ private llama;
13
+ private model;
14
+ private context;
15
+ private sequence;
16
+ private session;
17
+ private readonly modelPath;
18
+ private readonly config;
19
+ private readonly chatWrapperName?;
20
+ private readonly gpuBackend?;
21
+ private readonly onProgress?;
22
+ private readonly signal?;
23
+ constructor(options: LlamaEngineOptions);
24
+ private get gpuOption();
25
+ static detectSupportedGpus(): Promise<string[]>;
26
+ init(): Promise<void>;
27
+ loadModel(onProgress?: (pct: number) => void): Promise<void>;
28
+ createContext(): Promise<void>;
29
+ createSession(systemPrompt: string): LlamaChatSession;
30
+ /** Konuşma geçmişini temizler ve yeni sistem promptu ile taze bir oturum kurar */
31
+ resetSession(systemPrompt: string): Promise<LlamaChatSession>;
32
+ getSession(): LlamaChatSession | undefined;
33
+ get contextSize(): number;
34
+ getUsedContextTokens(): number;
35
+ get sequenceInstalled(): boolean;
36
+ dispose(): Promise<void>;
37
+ }
38
+ export declare function resolveChatWrapper(name: ChatWrapperName): "auto" | ChatWrapper;
39
+ export type { ChatSessionModelFunctions };
@@ -0,0 +1,146 @@
1
+ import { getLlama, getLlamaGpuTypes, LlamaChatSession, Gemma4ChatWrapper, GemmaChatWrapper, QwenChatWrapper, Llama3_1ChatWrapper, MistralChatWrapper, ChatMLChatWrapper, FunctionaryChatWrapper, LlamaLogLevel } from "node-llama-cpp";
2
+ import { logger } from "../utils/paths.js";
3
+ export class LlamaEngine {
4
+ llama;
5
+ model;
6
+ context;
7
+ sequence;
8
+ session;
9
+ modelPath;
10
+ config;
11
+ chatWrapperName;
12
+ gpuBackend;
13
+ onProgress;
14
+ signal;
15
+ constructor(options) {
16
+ this.modelPath = options.modelPath;
17
+ this.config = options.config;
18
+ this.chatWrapperName = options.chatWrapperName;
19
+ this.gpuBackend = options.gpuBackend ?? options.config.gpu;
20
+ this.onProgress = options.onProgress;
21
+ this.signal = options.signal;
22
+ }
23
+ get gpuOption() {
24
+ if (!this.gpuBackend)
25
+ return undefined;
26
+ if (this.gpuBackend === "auto")
27
+ return { type: "auto" };
28
+ return undefined;
29
+ }
30
+ static async detectSupportedGpus() {
31
+ try {
32
+ const supported = await getLlamaGpuTypes("supported");
33
+ return supported.filter((g) => typeof g === "string");
34
+ }
35
+ catch {
36
+ return [];
37
+ }
38
+ }
39
+ async init() {
40
+ if (this.llama)
41
+ return;
42
+ logger.info(`Motor baslatiliyor... (model: ${this.modelPath})`);
43
+ const supportedGpus = await LlamaEngine.detectSupportedGpus();
44
+ logger.debug(`Desteklenen GPU'lar: ${supportedGpus.join(", ") || "yok (CPU)"}`);
45
+ this.llama = await getLlama({
46
+ gpu: this.gpuOption,
47
+ logLevel: LlamaLogLevel.error,
48
+ progressLogs: "stderr",
49
+ skipDownload: true
50
+ });
51
+ }
52
+ async loadModel(onProgress) {
53
+ await this.init();
54
+ if (this.model)
55
+ return;
56
+ logger.info(`Model yukleniyor: ${this.modelPath}`);
57
+ this.model = await this.llama.loadModel({
58
+ modelPath: this.modelPath,
59
+ gpuLayers: this.config.gpuLayers ?? "auto",
60
+ onLoadProgress: (pct) => {
61
+ const percentage = Math.min(100, Math.max(0, Math.round(pct * 100)));
62
+ onProgress?.(percentage);
63
+ this.onProgress?.(percentage);
64
+ },
65
+ loadSignal: this.signal
66
+ });
67
+ }
68
+ async createContext() {
69
+ await this.loadModel();
70
+ if (this.context)
71
+ return;
72
+ this.context = await this.model.createContext({
73
+ contextSize: this.config.contextSize ?? "auto"
74
+ });
75
+ this.sequence = this.context.getSequence();
76
+ }
77
+ createSession(systemPrompt) {
78
+ if (!this.sequence)
79
+ throw new Error("Oturum olusturmadan once context olusturulmali (createContext)");
80
+ const wrapper = resolveChatWrapper(this.chatWrapperName ?? "auto");
81
+ this.session = new LlamaChatSession({
82
+ contextSequence: this.sequence,
83
+ systemPrompt,
84
+ chatWrapper: wrapper
85
+ });
86
+ return this.session;
87
+ }
88
+ /** Konuşma geçmişini temizler ve yeni sistem promptu ile taze bir oturum kurar */
89
+ async resetSession(systemPrompt) {
90
+ if (!this.sequence)
91
+ throw new Error("Oturum olusturmadan once context olusturulmali (createContext)");
92
+ await this.session?.dispose();
93
+ await this.sequence.clearHistory();
94
+ return this.createSession(systemPrompt);
95
+ }
96
+ getSession() {
97
+ return this.session;
98
+ }
99
+ get contextSize() {
100
+ return this.context?.contextSize ?? 0;
101
+ }
102
+ getUsedContextTokens() {
103
+ return this.sequence?.nextTokenIndex ?? 0;
104
+ }
105
+ get sequenceInstalled() {
106
+ return this.sequence != null;
107
+ }
108
+ async dispose() {
109
+ try {
110
+ this.session?.dispose();
111
+ this.context?.dispose();
112
+ this.model?.dispose();
113
+ this.llama?.dispose();
114
+ }
115
+ catch {
116
+ /* dispose hatalari yut */
117
+ }
118
+ this.session = undefined;
119
+ this.context = undefined;
120
+ this.model = undefined;
121
+ this.llama = undefined;
122
+ this.sequence = undefined;
123
+ }
124
+ }
125
+ export function resolveChatWrapper(name) {
126
+ switch (name) {
127
+ case "gemma4":
128
+ return new Gemma4ChatWrapper({ reasoning: true });
129
+ case "gemma":
130
+ return new GemmaChatWrapper();
131
+ case "qwen3":
132
+ return new QwenChatWrapper();
133
+ case "llama3":
134
+ return new Llama3_1ChatWrapper();
135
+ case "mistral":
136
+ return new MistralChatWrapper();
137
+ case "chatml":
138
+ return new ChatMLChatWrapper();
139
+ case "functionary":
140
+ return new FunctionaryChatWrapper();
141
+ case "auto":
142
+ default:
143
+ return "auto";
144
+ }
145
+ }
146
+ //# sourceMappingURL=llama.js.map
@@ -0,0 +1,18 @@
1
+ import type { ModelDefinition } from "../config/types.js";
2
+ export type DownloadProgress = {
3
+ totalSize: number;
4
+ downloadedSize: number;
5
+ estimatedTimeLeft: number;
6
+ averageSpeed: number;
7
+ percent?: number;
8
+ };
9
+ export type DownloadOptions = {
10
+ onProgress?: (progress: DownloadProgress) => void;
11
+ showProgress?: boolean;
12
+ signal?: AbortSignal;
13
+ };
14
+ export declare function formatBytes(bytes: number): string;
15
+ export declare function formatTime(ms: number): string;
16
+ export declare function downloadModel(model: ModelDefinition, options?: DownloadOptions): Promise<string>;
17
+ export declare function getModelFileSize(path: string): number;
18
+ export declare function isDownloaded(model: ModelDefinition): boolean;