openaxies 0.4.0 → 0.5.1
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/package.json +1 -3
- package/src/App.js +271 -263
- package/src/components/ComposerDock.js +23 -74
- package/src/config/models.js +11 -5
- package/src/providers/index.js +16 -12
- package/src/providers/streaming.js +92 -96
- package/src/components/BrandHeader.js +0 -60
- package/src/components/ChatViewport.js +0 -193
- package/src/components/RouterBar.js +0 -98
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Box, Text } from 'ink';
|
|
3
|
-
import { hex } from '../config/theme.js';
|
|
4
|
-
|
|
5
|
-
const h = React.createElement;
|
|
6
|
-
|
|
7
|
-
const THINK_OPEN = '<think>';
|
|
8
|
-
const THINK_CLOSE = '</think>';
|
|
9
|
-
|
|
10
|
-
function checkArray(arr) {
|
|
11
|
-
if (Array.isArray(arr) === false) {
|
|
12
|
-
return [];
|
|
13
|
-
}
|
|
14
|
-
return arr;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function clampWidth(width) {
|
|
18
|
-
if (typeof width !== 'number' || width < 20) {
|
|
19
|
-
return 80;
|
|
20
|
-
}
|
|
21
|
-
return Math.max(20, width - 2);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function pushLine(lines, text, color, bold) {
|
|
25
|
-
lines.push({
|
|
26
|
-
text: typeof text === 'string' ? text : '',
|
|
27
|
-
color: color,
|
|
28
|
-
bold: bold === true,
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function wrapText(text, width) {
|
|
33
|
-
const safeWidth = Math.max(1, width);
|
|
34
|
-
const raw = typeof text === 'string' ? text : '';
|
|
35
|
-
const sourceLines = raw.split('\n');
|
|
36
|
-
const wrapped = [];
|
|
37
|
-
|
|
38
|
-
for (let i = 0; i < sourceLines.length; i++) {
|
|
39
|
-
let line = sourceLines[i];
|
|
40
|
-
if (line.length === 0) {
|
|
41
|
-
wrapped.push('');
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
while (line.length > safeWidth) {
|
|
46
|
-
let cut = line.lastIndexOf(' ', safeWidth);
|
|
47
|
-
if (cut < Math.floor(safeWidth / 2)) {
|
|
48
|
-
cut = safeWidth;
|
|
49
|
-
}
|
|
50
|
-
wrapped.push(line.slice(0, cut));
|
|
51
|
-
line = line.slice(cut).trimStart();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
wrapped.push(line);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return wrapped;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function pushWrapped(lines, text, width, color, bold) {
|
|
61
|
-
const wrapped = wrapText(text, width);
|
|
62
|
-
for (let i = 0; i < wrapped.length; i++) {
|
|
63
|
-
pushLine(lines, wrapped[i], color, bold);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function stripThinkTags(text) {
|
|
68
|
-
if (typeof text !== 'string') {
|
|
69
|
-
return '';
|
|
70
|
-
}
|
|
71
|
-
return text.split(THINK_OPEN).join('').split(THINK_CLOSE).join('');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function extractThinkContent(text) {
|
|
75
|
-
if (typeof text !== 'string') {
|
|
76
|
-
return '';
|
|
77
|
-
}
|
|
78
|
-
const openIdx = text.indexOf(THINK_OPEN);
|
|
79
|
-
if (openIdx === -1) {
|
|
80
|
-
return '';
|
|
81
|
-
}
|
|
82
|
-
const closeIdx = text.indexOf(THINK_CLOSE, openIdx + THINK_OPEN.length);
|
|
83
|
-
if (closeIdx === -1) {
|
|
84
|
-
return text.slice(openIdx + THINK_OPEN.length);
|
|
85
|
-
}
|
|
86
|
-
return text.slice(openIdx + THINK_OPEN.length, closeIdx);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function extractAfterThink(text) {
|
|
90
|
-
if (typeof text !== 'string') {
|
|
91
|
-
return '';
|
|
92
|
-
}
|
|
93
|
-
const closeIdx = text.lastIndexOf(THINK_CLOSE);
|
|
94
|
-
if (closeIdx === -1) {
|
|
95
|
-
return '';
|
|
96
|
-
}
|
|
97
|
-
return text.slice(closeIdx + THINK_CLOSE.length);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function pushRawMessage(lines, msg, safeCols) {
|
|
101
|
-
if (msg === null || msg === undefined || typeof msg !== 'object') {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const content = typeof msg.content === 'string' ? msg.content : '';
|
|
106
|
-
if (content.length === 0) {
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const clean = stripThinkTags(content);
|
|
111
|
-
if (clean.length === 0) {
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (msg.role === 'user') {
|
|
116
|
-
pushWrapped(lines, clean, safeCols, hex.neonBlue, false);
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
pushWrapped(lines, clean, safeCols, hex.primary, false);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function pushSpinnerLine(lines, spinnerChar, safeCols) {
|
|
124
|
-
const safeChar = typeof spinnerChar === 'string' && spinnerChar.length > 0
|
|
125
|
-
? spinnerChar
|
|
126
|
-
: '◐';
|
|
127
|
-
pushLine(lines, ' ' + safeChar + ' thinking', '#FF9F43', true);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export function createChatViewport(messages, streamText, availLines, cols, scrollOffset, isThinking, spinnerChar) {
|
|
131
|
-
const safeMessages = checkArray(messages);
|
|
132
|
-
const safeAvail = typeof availLines === 'number' && availLines > 0 ? availLines : 1;
|
|
133
|
-
const safeCols = clampWidth(cols);
|
|
134
|
-
const rawOffset = typeof scrollOffset === 'number' && scrollOffset > 0
|
|
135
|
-
? Math.floor(scrollOffset)
|
|
136
|
-
: 0;
|
|
137
|
-
const hasStream = typeof streamText === 'string' && streamText.length > 0;
|
|
138
|
-
const isStreamingThink = isThinking === true && hasStream === true;
|
|
139
|
-
const transcriptLines = [];
|
|
140
|
-
|
|
141
|
-
for (let i = 0; i < safeMessages.length; i++) {
|
|
142
|
-
pushRawMessage(transcriptLines, safeMessages[i], safeCols);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (hasStream === true) {
|
|
146
|
-
if (isStreamingThink === true) {
|
|
147
|
-
pushSpinnerLine(transcriptLines, spinnerChar, safeCols);
|
|
148
|
-
const thinkContent = extractThinkContent(streamText);
|
|
149
|
-
if (thinkContent.length > 0) {
|
|
150
|
-
pushWrapped(transcriptLines, thinkContent, safeCols, '#FF9F43', false);
|
|
151
|
-
}
|
|
152
|
-
} else {
|
|
153
|
-
const clean = stripThinkTags(streamText);
|
|
154
|
-
if (clean.length > 0) {
|
|
155
|
-
pushWrapped(transcriptLines, clean, safeCols, hex.primary, false);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (transcriptLines.length === 0) {
|
|
161
|
-
pushLine(transcriptLines, ' awaiting input...', '#333355', false);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const visibleHeight = Math.max(1, safeAvail - 1);
|
|
165
|
-
const maxOffset = Math.max(0, transcriptLines.length - visibleHeight);
|
|
166
|
-
const safeOffset = Math.min(rawOffset, maxOffset);
|
|
167
|
-
const endIndex = transcriptLines.length - safeOffset;
|
|
168
|
-
const startIndex = Math.max(0, endIndex - visibleHeight);
|
|
169
|
-
const visibleLines = transcriptLines.slice(startIndex, endIndex);
|
|
170
|
-
const elements = [];
|
|
171
|
-
|
|
172
|
-
for (let i = 0; i < visibleLines.length; i++) {
|
|
173
|
-
const line = visibleLines[i];
|
|
174
|
-
elements.push(
|
|
175
|
-
h(Text, {
|
|
176
|
-
key: 'line-' + (startIndex + i),
|
|
177
|
-
color: line.color,
|
|
178
|
-
bold: line.bold,
|
|
179
|
-
}, line.text)
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return h(Box, {
|
|
184
|
-
flexGrow: 1,
|
|
185
|
-
height: safeAvail,
|
|
186
|
-
flexDirection: 'column',
|
|
187
|
-
overflow: 'hidden',
|
|
188
|
-
paddingLeft: 1,
|
|
189
|
-
paddingRight: 1,
|
|
190
|
-
paddingTop: 0,
|
|
191
|
-
paddingBottom: 1,
|
|
192
|
-
}, ...elements);
|
|
193
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Box, Text } from 'ink';
|
|
3
|
-
import { hex } from '../config/theme.js';
|
|
4
|
-
|
|
5
|
-
const h = React.createElement;
|
|
6
|
-
|
|
7
|
-
export const ROUTER_HEIGHT = 2;
|
|
8
|
-
|
|
9
|
-
function checkActiveModel(modelId) {
|
|
10
|
-
if (typeof modelId !== 'string') {
|
|
11
|
-
return 'openaxis/openaxis-flash';
|
|
12
|
-
}
|
|
13
|
-
return modelId;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function checkColumns(cols) {
|
|
17
|
-
if (typeof cols !== 'number' || cols < 1) {
|
|
18
|
-
return 80;
|
|
19
|
-
}
|
|
20
|
-
return cols;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function buildSeparator(cols) {
|
|
24
|
-
const safeCols = checkColumns(cols);
|
|
25
|
-
let line = '';
|
|
26
|
-
for (let i = 0; i < safeCols; i++) {
|
|
27
|
-
line = line + '\u2500';
|
|
28
|
-
}
|
|
29
|
-
return line;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// RouterBar — 2 rows:
|
|
33
|
-
// Row 1: model status with active/standby indicators
|
|
34
|
-
// Row 2: full-width separator line
|
|
35
|
-
export function createRouterBar(activeModel, cols) {
|
|
36
|
-
const modelStr = checkActiveModel(activeModel);
|
|
37
|
-
const isFlash = modelStr === 'openaxis/openaxis-flash';
|
|
38
|
-
const sepLine = buildSeparator(cols);
|
|
39
|
-
|
|
40
|
-
return h(Box, {
|
|
41
|
-
flexDirection: 'column',
|
|
42
|
-
width: '100%',
|
|
43
|
-
height: ROUTER_HEIGHT,
|
|
44
|
-
backgroundColor: hex.black,
|
|
45
|
-
paddingLeft: 1,
|
|
46
|
-
paddingRight: 1,
|
|
47
|
-
flexShrink: 0,
|
|
48
|
-
},
|
|
49
|
-
h(Box, {
|
|
50
|
-
height: 1,
|
|
51
|
-
flexDirection: 'row',
|
|
52
|
-
alignItems: 'center',
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
// ── Mixtral slot ──
|
|
56
|
-
h(Text, {
|
|
57
|
-
color: isFlash ? hex.neonBlue : '#333355',
|
|
58
|
-
bold: isFlash,
|
|
59
|
-
}, isFlash ? '\u25B6 ' : '\u25B7 '),
|
|
60
|
-
h(Text, {
|
|
61
|
-
color: isFlash ? hex.primary : '#444466',
|
|
62
|
-
bold: isFlash,
|
|
63
|
-
}, 'Flash'),
|
|
64
|
-
h(Text, {
|
|
65
|
-
color: isFlash ? hex.greenOnline : '#2A3A2A',
|
|
66
|
-
}, ' \u25CF '),
|
|
67
|
-
h(Text, {
|
|
68
|
-
color: isFlash ? hex.greenOnline : '#2A3A2A',
|
|
69
|
-
dimColor: !isFlash,
|
|
70
|
-
}, isFlash ? 'ACTIVE' : 'STANDBY'),
|
|
71
|
-
|
|
72
|
-
// ── Divider ──
|
|
73
|
-
h(Text, { color: '#1E1E2E' }, ' \u2502 '),
|
|
74
|
-
|
|
75
|
-
// ── Heavy slot ──
|
|
76
|
-
h(Text, {
|
|
77
|
-
color: !isFlash ? hex.neonBlue : '#333355',
|
|
78
|
-
bold: !isFlash,
|
|
79
|
-
}, !isFlash ? '\u25B6 ' : '\u25B7 '),
|
|
80
|
-
h(Text, {
|
|
81
|
-
color: !isFlash ? hex.primary : '#444466',
|
|
82
|
-
bold: !isFlash,
|
|
83
|
-
}, 'Heavy'),
|
|
84
|
-
h(Text, {
|
|
85
|
-
color: !isFlash ? hex.greenOnline : '#2A3A2A',
|
|
86
|
-
}, ' \u25CF '),
|
|
87
|
-
h(Text, {
|
|
88
|
-
color: !isFlash ? hex.greenOnline : '#2A3A2A',
|
|
89
|
-
dimColor: isFlash,
|
|
90
|
-
}, !isFlash ? 'ACTIVE' : 'STANDBY'),
|
|
91
|
-
|
|
92
|
-
// ── Right-side hint ──
|
|
93
|
-
h(Text, { color: '#1E1E2E' }, ' \u2502 '),
|
|
94
|
-
h(Text, { color: '#2A2A40' }, '/model to switch'),
|
|
95
|
-
),
|
|
96
|
-
h(Text, { color: '#1A1A28' }, sepLine)
|
|
97
|
-
);
|
|
98
|
-
}
|