openaxies 1.0.2 → 1.0.4
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/bin/openaxis.js +1 -50
- package/package.json +1 -1
- package/src/App.js +173 -277
- package/src/providers/index.js +3 -0
- package/src/providers/streaming.js +2 -2
package/bin/openaxis.js
CHANGED
|
@@ -1,58 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { render } from 'ink';
|
|
4
|
-
import fs from 'fs';
|
|
5
|
-
import path from 'path';
|
|
6
4
|
import App from '../src/App.js';
|
|
7
5
|
|
|
8
6
|
const h = React.createElement;
|
|
9
7
|
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
function sandboxWrite(chunk) {
|
|
13
|
-
const str = typeof chunk === 'string' ? chunk : String(chunk);
|
|
14
|
-
const trimmed = str.trim();
|
|
15
|
-
if (trimmed.length > 0) {
|
|
16
|
-
debugBuffer.push(trimmed);
|
|
17
|
-
}
|
|
18
|
-
return true;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const consoleKeys = ['log', 'error', 'warn'];
|
|
22
|
-
for (let i = 0; i < consoleKeys.length; i++) {
|
|
23
|
-
const key = consoleKeys[i];
|
|
24
|
-
const original = console[key];
|
|
25
|
-
console[key] = function sandboxedConsole() {
|
|
26
|
-
const args = [];
|
|
27
|
-
for (let j = 0; j < arguments.length; j++) {
|
|
28
|
-
args.push(arguments[j]);
|
|
29
|
-
}
|
|
30
|
-
sandboxWrite(args.join(' '));
|
|
31
|
-
return original.apply(console, args);
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const { waitUntilExit } = render(h(App, {}));
|
|
36
|
-
// Keep the process alive until the Ink UI signals exit (e.g., via SIGINT).
|
|
8
|
+
const { waitUntilExit } = render(h(App, {}), { exitOnCtrlC: false });
|
|
37
9
|
waitUntilExit();
|
|
38
|
-
|
|
39
|
-
function cleanup() {
|
|
40
|
-
if (debugBuffer.length > 0) {
|
|
41
|
-
const logPath = path.join(process.cwd(), 'openaxies-debug.log');
|
|
42
|
-
try {
|
|
43
|
-
fs.writeFileSync(logPath, debugBuffer.join('\n') + '\n');
|
|
44
|
-
} catch (_) {}
|
|
45
|
-
}
|
|
46
|
-
process.exit(0);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
process.on('SIGINT', cleanup);
|
|
50
|
-
process.on('SIGTERM', cleanup);
|
|
51
|
-
process.on('exit', function () {
|
|
52
|
-
if (debugBuffer.length > 0) {
|
|
53
|
-
const logPath = path.join(process.cwd(), 'openaxies-debug.log');
|
|
54
|
-
try {
|
|
55
|
-
fs.writeFileSync(logPath, debugBuffer.join('\n') + '\n');
|
|
56
|
-
} catch (_) {}
|
|
57
|
-
}
|
|
58
|
-
});
|
package/package.json
CHANGED
package/src/App.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Box, Text, useInput, useStdout } from 'ink';
|
|
2
|
+
import { Box, Text, useInput, useStdout, useApp } from 'ink';
|
|
3
3
|
import TextInput from 'ink-text-input';
|
|
4
4
|
import { hex } from './config/theme.js';
|
|
5
5
|
import { getModels, getModelById, DEFAULT_MODEL_ID } from './config/models.js';
|
|
@@ -7,7 +7,7 @@ import { callModel } from './providers/index.js';
|
|
|
7
7
|
|
|
8
8
|
const h = React.createElement;
|
|
9
9
|
|
|
10
|
-
const
|
|
10
|
+
const STARTUP = [
|
|
11
11
|
' \u2588\u2588\u2588\u2588\u2588\u2588\u2557',
|
|
12
12
|
' \u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557',
|
|
13
13
|
' \u2588\u2588\u2551 \u2588\u2588\u2551',
|
|
@@ -15,352 +15,248 @@ const STARTUP_LOGO = [
|
|
|
15
15
|
' \u255a\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255d',
|
|
16
16
|
];
|
|
17
17
|
|
|
18
|
-
const
|
|
19
|
-
{ id: 'model',
|
|
20
|
-
{ id: 'thoughts',
|
|
21
|
-
{ id: 'resume',
|
|
22
|
-
{ id: 'clear',
|
|
23
|
-
{ id: 'help',
|
|
24
|
-
{ id: 'exit',
|
|
18
|
+
const CMDS = [
|
|
19
|
+
{ id: 'model', t: '/model', d: 'Switch model' },
|
|
20
|
+
{ id: 'thoughts', t: '/thoughts', d: 'Toggle thinking' },
|
|
21
|
+
{ id: 'resume', t: '/resume', d: 'Re-run last query' },
|
|
22
|
+
{ id: 'clear', t: '/clear', d: 'Clear conversation' },
|
|
23
|
+
{ id: 'help', t: '/help', d: 'Show commands' },
|
|
24
|
+
{ id: 'exit', t: '/exit', d: 'Quit' },
|
|
25
25
|
];
|
|
26
26
|
|
|
27
27
|
export default function AppRoot() {
|
|
28
28
|
const { stdout } = useStdout();
|
|
29
|
-
const
|
|
29
|
+
const { exit } = useApp();
|
|
30
30
|
const cols = stdout.columns || 80;
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
const [
|
|
35
|
-
const [
|
|
36
|
-
const [
|
|
37
|
-
const [
|
|
38
|
-
const [
|
|
39
|
-
const [
|
|
40
|
-
const [
|
|
41
|
-
const [
|
|
42
|
-
const [
|
|
43
|
-
const [
|
|
44
|
-
const [
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
const
|
|
31
|
+
const rows = stdout.rows || 24;
|
|
32
|
+
const WID = Math.min(cols - 4, 56);
|
|
33
|
+
|
|
34
|
+
const [msgs, setMsgs] = React.useState([]);
|
|
35
|
+
const [model, setModel] = React.useState(DEFAULT_MODEL_ID);
|
|
36
|
+
const [inp, setInp] = React.useState('');
|
|
37
|
+
const [busy, setBusy] = React.useState(false);
|
|
38
|
+
const [stream, setStream] = React.useState('');
|
|
39
|
+
const [think, setThink] = React.useState(false);
|
|
40
|
+
const [thinkTxt, setThinkTxt] = React.useState('');
|
|
41
|
+
const [thinkSec, setThinkSec] = React.useState(0);
|
|
42
|
+
const [ready, setReady] = React.useState(false);
|
|
43
|
+
const [overlay, setOverlay] = React.useState(false);
|
|
44
|
+
const [ovIdx, setOvIdx] = React.useState(0);
|
|
45
|
+
const [showT, setShowT] = React.useState(true);
|
|
46
|
+
const [tool, setTool] = React.useState(null);
|
|
47
|
+
|
|
48
|
+
const acRef = React.useRef(null);
|
|
49
|
+
const accRef = React.useRef('');
|
|
50
|
+
const lastRef = React.useRef('');
|
|
49
51
|
|
|
50
52
|
React.useEffect(() => {
|
|
51
|
-
if (!
|
|
52
|
-
const t = setInterval(() =>
|
|
53
|
+
if (!think) return;
|
|
54
|
+
const t = setInterval(() => setThinkSec(p => p + 0.1), 100);
|
|
53
55
|
return () => clearInterval(t);
|
|
54
|
-
}, [
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
function
|
|
60
|
-
|
|
56
|
+
}, [think]);
|
|
57
|
+
|
|
58
|
+
const mi = getModelById(model);
|
|
59
|
+
const label = mi ? mi.label.replace('OpenAxies ', '') : 'llama';
|
|
60
|
+
|
|
61
|
+
function strip(s) { return showT ? s : s.split('<think>').join('').split('</think>').join(''); }
|
|
62
|
+
|
|
63
|
+
function headBox() {
|
|
64
|
+
const R = '\u2502';
|
|
65
|
+
const D = '\u2500';
|
|
66
|
+
const W = WID;
|
|
67
|
+
const hd = function (s) { return R + ' ' + s + ' '.repeat(Math.max(0, W - s.length)) + ' ' + R; };
|
|
68
|
+
return h(Box, { flexShrink: 0, flexDirection: 'column', paddingLeft: 1, paddingRight: 1 },
|
|
69
|
+
h(Text, { color: '#555577' }, '\u250C' + D.repeat(W + 2) + '\u2510'),
|
|
70
|
+
h(Text, { color: '#555577' }, hd('\u203A_ OpenAxies (v1.0.3)')),
|
|
71
|
+
h(Text, { color: '#555577' }, hd('')),
|
|
72
|
+
h(Text, { color: '#555577' }, hd('model: ' + label + ' /model to change')),
|
|
73
|
+
h(Text, { color: '#555577' }, hd('directory: ~')),
|
|
74
|
+
h(Text, { color: '#555577' }, '\u2514' + D.repeat(W + 2) + '\u2518'),
|
|
75
|
+
);
|
|
61
76
|
}
|
|
62
77
|
|
|
63
|
-
async function
|
|
64
|
-
const
|
|
65
|
-
if (!
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
setStreamText('');
|
|
71
|
-
setThinkingText('');
|
|
72
|
-
setThinkElapsed(0);
|
|
73
|
-
setToolEvent(null);
|
|
74
|
-
setMessages(p => [...p, { type: 'user', content: s }]);
|
|
75
|
-
setInput('');
|
|
76
|
-
|
|
77
|
-
const m = getModelById(activeModel);
|
|
78
|
-
if (!m) { setMessages(p => [...p, { type: 'assistant', content: 'No model loaded.' }]); setStreaming(false); return; }
|
|
78
|
+
async function submit(s) {
|
|
79
|
+
const t = typeof s === 'string' ? s.trim() : '';
|
|
80
|
+
if (!t || busy) return;
|
|
81
|
+
setReady(true); lastRef.current = t; accRef.current = '';
|
|
82
|
+
setBusy(true); setStream(''); setThinkTxt(''); setThinkSec(0); setTool(null);
|
|
83
|
+
setMsgs(p => [...p, { role: 'user', c: t }]);
|
|
84
|
+
setInp('');
|
|
79
85
|
|
|
86
|
+
const m = getModelById(model);
|
|
87
|
+
if (!m) { setMsgs(p => [...p, { role: 'asst', c: 'No model loaded.' }]); setBusy(false); return; }
|
|
80
88
|
const ac = new AbortController();
|
|
81
|
-
|
|
89
|
+
acRef.current = ac;
|
|
90
|
+
let op = false;
|
|
82
91
|
|
|
83
92
|
try {
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
if (msg.type === 'user') history.push({ role: 'user', content: msg.content });
|
|
87
|
-
else if (msg.type === 'assistant' || msg.type === 'thought') history.push({ role: 'assistant', content: msg.content });
|
|
88
|
-
}
|
|
89
|
-
history.push({ role: 'user', content: s });
|
|
93
|
+
const hist = msgs.map(x => ({ role: x.role === 'user' ? 'user' : 'assistant', content: x.c || '' }));
|
|
94
|
+
hist.push({ role: 'user', content: t });
|
|
90
95
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
for await (const ev of callModel({ id: activeModel }, history, ac.signal)) {
|
|
96
|
+
for await (const ev of callModel({ id: model }, hist, ac.signal)) {
|
|
94
97
|
if (ac.signal.aborted) break;
|
|
95
|
-
|
|
96
|
-
if (ev.type === 'tool') {
|
|
97
|
-
setToolEvent({ tool: ev.tool, query: ev.query, sites: ev.sites });
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
|
|
98
|
+
if (ev.type === 'tool') { setTool({ tool: ev.tool, q: ev.query, sites: ev.sites }); continue; }
|
|
101
99
|
if (ev.type === 'token' || ev.type === 'thinking') {
|
|
102
100
|
const c = typeof ev.content === 'string' ? ev.content : '';
|
|
103
101
|
if (!c) continue;
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (stillOpen) {
|
|
113
|
-
setThinkingActive(true);
|
|
114
|
-
setThinkingText(clean);
|
|
115
|
-
setStreamText('');
|
|
116
|
-
} else {
|
|
117
|
-
thinkOpen = false;
|
|
118
|
-
setThinkingActive(false);
|
|
119
|
-
setStreamText(clean);
|
|
120
|
-
setThinkingText('');
|
|
121
|
-
}
|
|
122
|
-
} else {
|
|
123
|
-
setStreamText(clean);
|
|
124
|
-
}
|
|
102
|
+
if (c.includes('<think>')) op = true;
|
|
103
|
+
accRef.current += c;
|
|
104
|
+
const cl = strip(accRef.current);
|
|
105
|
+
if (op || accRef.current.includes('<think>')) {
|
|
106
|
+
const st = op || !accRef.current.includes('</think>');
|
|
107
|
+
if (st) { setThink(true); setThinkTxt(cl); setStream(''); }
|
|
108
|
+
else { op = false; setThink(false); setStream(cl); setThinkTxt(''); }
|
|
109
|
+
} else setStream(cl);
|
|
125
110
|
}
|
|
126
|
-
|
|
127
111
|
if (ev.type === 'done' || ev.type === 'timeout') {
|
|
128
|
-
if (ev.type === 'timeout' &&
|
|
129
|
-
accumulatedRef.current += '\n\u2014 timed out \u2014';
|
|
130
|
-
}
|
|
112
|
+
if (ev.type === 'timeout' && accRef.current) accRef.current += '\n\u2014timed out\u2014';
|
|
131
113
|
break;
|
|
132
114
|
}
|
|
133
115
|
}
|
|
134
|
-
} catch (
|
|
135
|
-
if (!ac.signal.aborted) {
|
|
136
|
-
setMessages(p => [...p, { type: 'assistant', content: 'Error: ' + (err.message || 'Connection failed') }]);
|
|
137
|
-
}
|
|
116
|
+
} catch (e) {
|
|
117
|
+
if (!ac.signal.aborted) setMsgs(p => [...p, { role: 'asst', c: 'Error: ' + (e.message || 'connection') }]);
|
|
138
118
|
} finally {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (final) {
|
|
144
|
-
setMessages(p => [...p, { type: 'assistant', content: stripThink(final) }]);
|
|
145
|
-
}
|
|
146
|
-
setStreamText('');
|
|
147
|
-
setThinkingText('');
|
|
119
|
+
setBusy(false); setThink(false); acRef.current = null;
|
|
120
|
+
const f = accRef.current;
|
|
121
|
+
if (f) setMsgs(p => [...p, { role: 'asst', c: strip(f) }]);
|
|
122
|
+
setStream(''); setThinkTxt('');
|
|
148
123
|
}
|
|
149
124
|
}
|
|
150
125
|
|
|
151
|
-
function
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
let found = false;
|
|
155
|
-
for (const m of models) {
|
|
156
|
-
if (found) return m.id;
|
|
157
|
-
if (m.id === p) found = true;
|
|
158
|
-
}
|
|
159
|
-
return models[0].id;
|
|
160
|
-
});
|
|
126
|
+
function cycle() {
|
|
127
|
+
const ms = getModels();
|
|
128
|
+
setModel(p => { let f = false; for (const m of ms) { if (f) return m.id; if (m.id === p) f = true; } return ms[0].id; });
|
|
161
129
|
}
|
|
162
130
|
|
|
163
|
-
function
|
|
131
|
+
function cmd(item) {
|
|
164
132
|
if (!item) return;
|
|
165
|
-
|
|
166
|
-
if (t === '/
|
|
167
|
-
if (t === '/
|
|
168
|
-
if (t === '/
|
|
169
|
-
if (t === '/
|
|
170
|
-
if (t === '/
|
|
171
|
-
|
|
172
|
-
setInput('');
|
|
173
|
-
setShowOverlay(false);
|
|
174
|
-
setOverlayIdx(0);
|
|
133
|
+
if (item.t === '/exit') { exit(); return; }
|
|
134
|
+
if (item.t === '/clear') setMsgs([]);
|
|
135
|
+
if (item.t === '/model') cycle();
|
|
136
|
+
if (item.t === '/thoughts') setShowT(p => !p);
|
|
137
|
+
if (item.t === '/resume') { const q = lastRef.current; if (q && !busy) submit(q); return; }
|
|
138
|
+
if (item.t === '/help') setMsgs(p => [...p, { role: 'asst', c: 'Commands: /help /clear /model /thoughts /resume /exit' }]);
|
|
139
|
+
setInp(''); setOverlay(false); setOvIdx(0);
|
|
175
140
|
}
|
|
176
141
|
|
|
177
|
-
useInput((
|
|
178
|
-
if (
|
|
179
|
-
if (
|
|
180
|
-
if (
|
|
181
|
-
if (
|
|
182
|
-
if (
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
if (!startupDone && !streaming) {
|
|
186
|
-
if (key.return) { setStartupDone(true); setInput(''); return; }
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
if (key.escape) {
|
|
190
|
-
if (streaming && abortRef.current) { abortRef.current.abort(); setStreaming(false); setStreamText(''); setThinkingActive(false); }
|
|
142
|
+
useInput((i, k) => {
|
|
143
|
+
if (overlay) {
|
|
144
|
+
if (k.escape) { setOverlay(false); setInp(''); setOvIdx(0); return; }
|
|
145
|
+
if (k.upArrow) setOvIdx(p => p > 0 ? p - 1 : CMDS.length - 1);
|
|
146
|
+
if (k.downArrow) setOvIdx(p => p < CMDS.length - 1 ? p + 1 : 0);
|
|
147
|
+
if (k.return) cmd(CMDS[ovIdx]);
|
|
191
148
|
return;
|
|
192
149
|
}
|
|
193
|
-
if (
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (
|
|
197
|
-
if (
|
|
198
|
-
if (
|
|
199
|
-
if (
|
|
150
|
+
if (!ready && !busy) { if (k.return) { setReady(true); setInp(''); } return; }
|
|
151
|
+
if (k.escape && busy) { if (acRef.current) acRef.current.abort(); setBusy(false); setStream(''); setThink(false); return; }
|
|
152
|
+
if (k.ctrl) {
|
|
153
|
+
if (i === 'c' || i === 'C') { exit(); return; }
|
|
154
|
+
if (i === 't' || i === 'T') { setShowT(p => !p); return; }
|
|
155
|
+
if (i === 'p' || i === 'P') { cycle(); return; }
|
|
156
|
+
if (i === 'r' || i === 'R') { const q = lastRef.current; if (q && !busy) submit(q); return; }
|
|
157
|
+
if (i === 'l' || i === 'L') { setMsgs([]); return; }
|
|
158
|
+
if (i === 'k' || i === 'K') { setInp('/'); setOverlay(true); setOvIdx(0); return; }
|
|
200
159
|
}
|
|
201
160
|
});
|
|
202
161
|
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
for (const msg of messages) {
|
|
206
|
-
if (msg.type === 'user' || msg.type === 'assistant' || msg.type === 'thought') {
|
|
207
|
-
events.push(msg);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Tool event
|
|
212
|
-
if (toolEvent && streaming) {
|
|
213
|
-
events.push({ type: 'tool', tool: toolEvent.tool, query: toolEvent.query });
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Live thinking
|
|
217
|
-
if (thinkingActive && thinkingText) {
|
|
218
|
-
events.push({ type: 'thought', content: thinkingText, elapsed: thinkElapsed.toFixed(1) + 's' });
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Live stream
|
|
222
|
-
if (streaming && streamText && !thinkingActive) {
|
|
223
|
-
events.push({ type: 'assistant', content: streamText });
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const visible = events.slice(-20);
|
|
227
|
-
|
|
228
|
-
// Startup screen
|
|
229
|
-
if (!startupDone && messages.length === 0) {
|
|
162
|
+
// Startup
|
|
163
|
+
if (!ready && msgs.length === 0) {
|
|
230
164
|
return h(Box, { flexDirection: 'column', width: '100%', height: rows, overflow: 'hidden' },
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
)
|
|
234
|
-
h(Box, {
|
|
235
|
-
h(Box, {
|
|
236
|
-
h(Box, {
|
|
237
|
-
h(Box, {
|
|
238
|
-
h(Box, { key: 'st3', height: 1, paddingLeft: 1 }, h(Text, { color: '#555577' }, 'Press Enter to begin')),
|
|
165
|
+
h(Box, { height: 3 }),
|
|
166
|
+
...STARTUP.map((l, i) => h(Box, { key: 'l' + i, height: 1, paddingLeft: 2 }, h(Text, { color: '#00BBFF', bold: true }, l))),
|
|
167
|
+
h(Box, { height: 1 }),
|
|
168
|
+
h(Box, { height: 1, paddingLeft: 2 }, h(Text, { color: '#FFFFFF', bold: true }, 'OpenAxies')),
|
|
169
|
+
h(Box, { height: 1, paddingLeft: 2 }, h(Text, { color: '#777788' }, 'local agent')),
|
|
170
|
+
h(Box, { height: 2 }),
|
|
171
|
+
h(Box, { height: 1, paddingLeft: 2 }, h(Text, { color: '#555577' }, 'Press Enter to begin')),
|
|
239
172
|
h(Box, { flexGrow: 1 }),
|
|
240
|
-
h(Box, {
|
|
173
|
+
h(Box, { height: 1, paddingLeft: 2 }, h(Text, { color: '#333344' }, 'esc quit ctrl+p models /help')),
|
|
241
174
|
);
|
|
242
175
|
}
|
|
243
176
|
|
|
244
|
-
//
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
177
|
+
// Build entries
|
|
178
|
+
const entries = [];
|
|
179
|
+
for (const m of msgs) {
|
|
180
|
+
if (m.role === 'user') entries.push({ t: 'user', c: m.c });
|
|
181
|
+
else if (m.role === 'asst') entries.push({ t: 'asst', c: m.c });
|
|
248
182
|
}
|
|
183
|
+
if (tool && busy) entries.push({ t: 'tool', tool: tool.tool, q: tool.q, sites: tool.sites });
|
|
184
|
+
if (think && thinkTxt) entries.push({ t: 'thought', c: thinkTxt, sec: thinkSec.toFixed(1) + 's' });
|
|
185
|
+
if (busy && stream && !think) entries.push({ t: 'asst', c: stream });
|
|
249
186
|
|
|
250
|
-
|
|
251
|
-
if (visible.length === 0) {
|
|
252
|
-
visible.push({ type: '_sep' });
|
|
253
|
-
visible.push({ type: '_ready' });
|
|
254
|
-
visible.push({ type: '_sep' });
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// Estimate lines
|
|
258
|
-
const HEADER_H = 1;
|
|
259
|
-
const SEP_H = 1;
|
|
260
|
-
const INPUT_H = 1;
|
|
261
|
-
const FOOTER_H = 1;
|
|
262
|
-
const fixed = HEADER_H + SEP_H + INPUT_H + FOOTER_H;
|
|
263
|
-
const maxLines = Math.max(5, rows - fixed);
|
|
264
|
-
|
|
265
|
-
function estLines(ev) {
|
|
266
|
-
if (ev.type === '_sep' || ev.type === '_ready' || ev.type === '_overlay') return 1;
|
|
267
|
-
if (ev.type === 'tool') return 1;
|
|
268
|
-
if (ev.type === 'user') return 2;
|
|
269
|
-
if (ev.type === 'thought') return 2;
|
|
270
|
-
return 1;
|
|
271
|
-
}
|
|
187
|
+
if (overlay) entries.push({ t: '_ov', idx: ovIdx });
|
|
272
188
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
for (let i = visible.length - 1; i >= 0; i--) {
|
|
276
|
-
const l = estLines(visible[i]);
|
|
277
|
-
if (totalEst + l > maxLines) break;
|
|
278
|
-
totalEst += l;
|
|
279
|
-
showN = visible.length - i;
|
|
280
|
-
}
|
|
281
|
-
const show = visible.slice(visible.length - showN);
|
|
189
|
+
const maxChat = Math.max(3, rows - 7);
|
|
190
|
+
const vis = entries.slice(-maxChat);
|
|
282
191
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
192
|
+
// Render chat
|
|
193
|
+
const chEls = [];
|
|
194
|
+
for (let i = 0; i < vis.length; i++) {
|
|
195
|
+
const e = vis[i];
|
|
286
196
|
const k = 'e' + i;
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
for (let ci = 0; ci < COMMANDS.length; ci++) {
|
|
295
|
-
const c = COMMANDS[ci];
|
|
296
|
-
const sel = ci === ev.idx;
|
|
297
|
-
const prefix = sel ? '\u276F ' : ' ';
|
|
298
|
-
const col = sel ? hex.neonBlue : '#666688';
|
|
299
|
-
els.push(h(Box, { key: k + 'c' + ci, height: 1, paddingLeft: 2 },
|
|
300
|
-
h(Text, { color: col }, prefix + c.trigger),
|
|
301
|
-
h(Text, { color: '#444466' }, ' ' + (c.desc || ''))
|
|
197
|
+
if (e.t === '_ov') {
|
|
198
|
+
chEls.push(h(Box, { key: k + 'h', height: 1, paddingLeft: 2 }, h(Text, { color: '#888899', bold: true }, 'Commands:')));
|
|
199
|
+
for (let ci = 0; ci < CMDS.length; ci++) {
|
|
200
|
+
const c = CMDS[ci]; const sel = ci === e.idx;
|
|
201
|
+
chEls.push(h(Box, { key: k + 'c' + ci, height: 1, paddingLeft: 3 },
|
|
202
|
+
h(Text, { color: sel ? hex.neonBlue : '#666688' }, (sel ? '\u276F ' : ' ') + c.t),
|
|
203
|
+
h(Text, { color: '#444466' }, ' ' + (c.d || ''))
|
|
302
204
|
));
|
|
303
205
|
}
|
|
304
|
-
} else if (
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
els.push(h(Box, { key: k, height: 1, paddingLeft: 1 }, h(Text, { color: '#FFFFFF', wrap: 'wrap' }, ev.content)));
|
|
309
|
-
} else if (ev.type === 'thought') {
|
|
310
|
-
els.push(h(Box, { key: k + 'h', height: 1, paddingLeft: 1 },
|
|
311
|
-
h(Text, { color: '#FF9F43', bold: true }, '\u25CB Thinking \u2022 ' + (ev.elapsed || '0.0s'))
|
|
206
|
+
} else if (e.t === 'user') {
|
|
207
|
+
chEls.push(h(Box, { key: k, height: 1, paddingLeft: 2 },
|
|
208
|
+
h(Text, { color: '#888899' }, '\u203A '),
|
|
209
|
+
h(Text, { color: hex.neonBlue, wrap: 'wrap' }, e.c),
|
|
312
210
|
));
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
h(Text, { color: '#
|
|
211
|
+
} else if (e.t === 'asst') {
|
|
212
|
+
chEls.push(h(Box, { key: k, height: 1, paddingLeft: 2 },
|
|
213
|
+
h(Text, { color: '#88FF88' }, '\u2022 '),
|
|
214
|
+
h(Text, { color: '#FFFFFF', wrap: 'wrap' }, e.c),
|
|
215
|
+
));
|
|
216
|
+
} else if (e.t === 'thought') {
|
|
217
|
+
chEls.push(h(Box, { key: k, height: 1, paddingLeft: 2 },
|
|
218
|
+
h(Text, { color: '#FF9F43', bold: true }, '\u2022 Thinking \u2022 ' + e.sec),
|
|
219
|
+
));
|
|
220
|
+
if (e.c) chEls.push(h(Box, { key: k + 'c', height: 1, paddingLeft: 4 }, h(Text, { color: '#FF9F43', wrap: 'wrap' }, e.c)));
|
|
221
|
+
} else if (e.t === 'tool') {
|
|
222
|
+
chEls.push(h(Box, { key: k, height: 1, paddingLeft: 2 },
|
|
223
|
+
h(Text, { color: '#5B5B8A' }, '\u2022 ' + (e.tool || 'tool') + (e.q ? ' "' + e.q + '"' : '') + (e.sites ? ' (' + e.sites + ' sites)' : '')),
|
|
317
224
|
));
|
|
318
225
|
}
|
|
319
226
|
}
|
|
320
227
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
els.push(h(Box, { key: 'f' + els.length, height: 1 }));
|
|
228
|
+
if (chEls.length === 0) {
|
|
229
|
+
chEls.push(h(Box, { key: 'idle', height: 1, paddingLeft: 2 }, h(Text, { color: '#666688' }, '\u203A Ask me anything')));
|
|
324
230
|
}
|
|
325
231
|
|
|
326
|
-
|
|
327
|
-
const header = h(Box, { height: HEADER_H, flexShrink: 0, paddingLeft: 1, paddingRight: 1 },
|
|
328
|
-
h(Text, { color: '#00BBFF', bold: true }, 'OpenAxies'),
|
|
329
|
-
h(Text, { color: '#555577' }, ' \u2022 '),
|
|
330
|
-
h(Text, { color: '#FFFFFF' }, modelLabel),
|
|
331
|
-
h(Box, { flexGrow: 1 }),
|
|
332
|
-
h(Text, { color: '#666688' }, 'local'),
|
|
333
|
-
);
|
|
334
|
-
|
|
335
|
-
// Separator
|
|
336
|
-
const sepLine = h(Box, { height: SEP_H, flexShrink: 0 }, h(Text, { color: '#1A1A28' }, '\u2500'.repeat(cols)));
|
|
232
|
+
const chatArea = h(Box, { flexGrow: 1, flexDirection: 'column', overflow: 'hidden' }, ...chEls);
|
|
337
233
|
|
|
338
|
-
// Input
|
|
339
|
-
const
|
|
234
|
+
// Input line
|
|
235
|
+
const inputBar = h(Box, { height: 1, flexShrink: 0, paddingLeft: 2, paddingRight: 2 },
|
|
236
|
+
h(Text, { color: '#888899' }, '\u203A '),
|
|
340
237
|
h(Text, { color: hex.greenOnline }, '\u258C'),
|
|
341
238
|
h(Box, { width: 1 }),
|
|
342
239
|
h(TextInput, {
|
|
343
|
-
value:
|
|
344
|
-
onChange:
|
|
345
|
-
onSubmit:
|
|
240
|
+
value: inp,
|
|
241
|
+
onChange: setInp,
|
|
242
|
+
onSubmit: v => { if (typeof v === 'string' && v.trim() && !busy) submit(v); },
|
|
346
243
|
placeholder: '',
|
|
347
|
-
focus: !
|
|
244
|
+
focus: !overlay,
|
|
348
245
|
showCursor: true,
|
|
349
|
-
})
|
|
246
|
+
}),
|
|
350
247
|
);
|
|
351
248
|
|
|
352
249
|
// Footer
|
|
353
|
-
const footer = h(Box, { height:
|
|
250
|
+
const footer = h(Box, { height: 1, flexShrink: 0, paddingLeft: 2, paddingRight: 2 },
|
|
354
251
|
h(Text, { color: '#444466' }, 'esc \u2248 ctrl+t ctrl+p ctrl+k'),
|
|
355
252
|
h(Box, { flexGrow: 1 }),
|
|
356
|
-
h(Text, { color: '#555577' },
|
|
253
|
+
h(Text, { color: '#555577' }, label.toLowerCase() + ' \u2022 ~'),
|
|
357
254
|
);
|
|
358
255
|
|
|
359
256
|
return h(Box, { flexDirection: 'column', width: '100%', height: rows, overflow: 'hidden' },
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
inputLine,
|
|
257
|
+
headBox(),
|
|
258
|
+
inputBar,
|
|
259
|
+
chatArea,
|
|
364
260
|
footer,
|
|
365
261
|
);
|
|
366
262
|
}
|
package/src/providers/index.js
CHANGED
|
@@ -4,12 +4,15 @@ import { runWebSearchGraph, shouldUseWebSearch } from './websearch.js';
|
|
|
4
4
|
const MODEL_ROUTES = Object.freeze({
|
|
5
5
|
'openaxis/openaxis-llama': Object.freeze([
|
|
6
6
|
'https://universal-618-clarity-main.hf.space/v1/chat/completions',
|
|
7
|
+
'https://universal-618-clarity-4.hf.space/v1/chat/completions',
|
|
7
8
|
]),
|
|
8
9
|
'openaxis/openaxis-gpt': Object.freeze([
|
|
9
10
|
'https://universal-618-clarity-2.hf.space/v1/chat/completions',
|
|
11
|
+
'https://universal-618-clarity-5.hf.space/v1/chat/completions',
|
|
10
12
|
]),
|
|
11
13
|
'openaxis/openaxis-deepseek': Object.freeze([
|
|
12
14
|
'https://universal-618-clarity-3.hf.space/v1/chat/completions',
|
|
15
|
+
'https://universal-618-clarity-6.hf.space/v1/chat/completions',
|
|
13
16
|
]),
|
|
14
17
|
});
|
|
15
18
|
|