ai: add <think> support

This commit is contained in:
end-4
2025-04-08 00:45:08 +02:00
parent 6d068b0a83
commit 1555f95185
3 changed files with 158 additions and 17 deletions
@@ -3,11 +3,12 @@ import GtkSource from "gi://GtkSource?version=3.0";
import App from 'resource:///com/github/Aylur/ags/app.js';
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
const { Box, Button, Label, Icon, Scrollable, Stack } = Widget;
const { Box, Button, Label, Icon, Revealer, Scrollable, Stack } = Widget;
const { execAsync, exec } = Utils;
import { MaterialIcon } from '../../.commonwidgets/materialicon.js';
import md2pango, { replaceInlineLatexWithCodeBlocks } from '../../.miscutils/md2pango.js';
import { darkMode } from "../../.miscutils/system.js";
import { setupCursorHover } from "../../.widgetutils/cursorhover.js";
const LATEX_DIR = `${GLib.get_user_cache_dir()}/ags/media/latex`;
const USERNAME = GLib.get_user_name();
@@ -45,20 +46,104 @@ const HighlightedCode = (content, lang) => {
return sourceView;
}
const TextBlock = (content = '') => Label({
hpack: 'fill',
className: 'txt sidebar-chat-txtblock sidebar-chat-txt',
useMarkup: true,
xalign: 0,
wrap: true,
selectable: true,
label: content,
});
const TextBlock = (content = '') => {
const widget = Label({
attribute: {
'updateTextPlain': (text) => {
widget.label = text;
},
'updateText': (text) => {
widget.attribute.updateTextPlain(md2pango(text));
}
},
hpack: 'fill',
className: 'txt sidebar-chat-txtblock sidebar-chat-txt',
useMarkup: true,
xalign: 0,
wrap: true,
selectable: true,
label: content,
});
return widget;
}
const ThinkBlock = (content = '', revealChild = true) => {
const revealThought = Variable(revealChild);
const mainText = Label({
hpack: 'fill',
className: `txt sidebar-chat-txtblock-think sidebar-chat-txt`,
useMarkup: true,
xalign: 0,
wrap: true,
selectable: true,
label: content,
});
const mainTextRevealer = Revealer({
transition: 'slide_down',
revealChild: revealThought.value,
child: mainText,
setup: (self) => self.hook(revealThought, (self) => {
self.revealChild = revealThought.value;
})
})
const expandIcon = MaterialIcon(revealThought.value ? 'expand_less' : 'expand_more', 'norm', {
setup: (self) => self.hook(revealThought, (self) => {
self.label = revealThought.value ? 'expand_less' : 'expand_more';
})
});
const widget = Box({
attribute: {
'updateTextPlain': (text) => {
mainText.label = text;
},
'updateText': (text) => {
widget.attribute.updateTextPlain(md2pango(text));
},
'done': () => {
revealThought.value = false;
}
},
className: 'sidebar-chat-thinkblock',
vertical: true,
children: [
Button({
onClicked: (self) => {
revealThought.value = !revealThought.value;
},
child: Box({
className: 'spacing-h-10 padding-10',
children: [
Box({
homogeneous: true,
valign: 'center',
className: 'sidebar-chat-thinkblock-icon',
children: [MaterialIcon('neurology', 'large')]
}),
Label({
valign: 'center',
hexpand: true,
label: 'Chain of Thought',
className: 'txt sidebar-chat-thinkblock-txt',
}),
Box({
className: 'sidebar-chat-thinkblock-btn-arrow',
homogeneous: true,
children: [expandIcon],
}),
]
}),
setup: setupCursorHover,
}),
mainTextRevealer,
]
});
return widget;
}
Utils.execAsync(['bash', '-c', `rm -rf ${LATEX_DIR}`])
.then(() => Utils.execAsync(['bash', '-c', `mkdir -p ${LATEX_DIR}`]))
.catch(print);
const Latex = (content = '') => {
const LatexBlock = (content = '') => {
const latexViewArea = Box({
// vscroll: 'never',
// hscroll: 'automatic',
@@ -122,7 +207,7 @@ sed -i 's/stroke="rgb(0%, 0%, 0%)"/stroke="${darkMode.value ? '#ffffff' : '#0000
const CodeBlock = (content = '', lang = 'txt') => {
if (lang == 'tex' || lang == 'latex') {
return Latex(content);
return LatexBlock(content);
}
const topBar = Box({
className: 'sidebar-chat-codeblock-topbar',
@@ -216,7 +301,7 @@ const MessageContent = (content) => {
child.destroy();
}
contentBox.add(TextBlock())
let lines = replaceInlineLatexWithCodeBlocks(content).split('\n');
let lastProcessed = 0;
let inCode = false;
@@ -228,7 +313,8 @@ const MessageContent = (content) => {
const lastLabel = kids[kids.length - 1];
const blockContent = lines.slice(lastProcessed, index).join('\n');
if (!inCode) {
lastLabel.label = md2pango(blockContent);
lastLabel.attribute.updateText(blockContent);
if (lastLabel.label == '') lastLabel.destroy();
contentBox.add(CodeBlock('', codeBlockRegex.exec(line)[1]));
}
else {
@@ -239,13 +325,31 @@ const MessageContent = (content) => {
lastProcessed = index + 1;
inCode = !inCode;
}
// Think block
const thinkBlockStartRegex = /^\s*<think>/; // Start: <think>
const thinkBlockEndRegex = /<\/think>\s*$/; // End: </think>
if (!inCode && (thinkBlockStartRegex.test(line) || thinkBlockEndRegex.test(line))) {
const kids = self.get_children();
const lastLabel = kids[kids.length - 1];
const blockContent = lines.slice(lastProcessed, index).join('\n');
lastLabel.attribute.updateTextPlain(blockContent);
if (lastLabel.label == '') lastLabel.destroy();
if (thinkBlockStartRegex.test(line)) contentBox.add(ThinkBlock());
else {
// lastLabel.attribute.done();
contentBox.add(TextBlock());
}
lastProcessed = index + 1;
}
// Breaks
const dividerRegex = /^\s*---/;
if (!inCode && dividerRegex.test(line)) {
const kids = self.get_children();
const lastLabel = kids[kids.length - 1];
const blockContent = lines.slice(lastProcessed, index).join('\n');
lastLabel.label = md2pango(blockContent);
lastLabel.attribute.updateTextPlain(blockContent);
contentBox.add(Divider());
contentBox.add(TextBlock());
lastProcessed = index + 1;
@@ -256,7 +360,7 @@ const MessageContent = (content) => {
const lastLabel = kids[kids.length - 1];
let blockContent = lines.slice(lastProcessed, lines.length).join('\n');
if (!inCode)
lastLabel.label = `${md2pango(blockContent)}${useCursor ? userOptions.ai.writingCursor : ''}`;
lastLabel.attribute.updateTextPlain(`${md2pango(blockContent)}${useCursor ? userOptions.ai.writingCursor : ''}`);
else
lastLabel.attribute.updateText(blockContent);
}
+1 -1
View File
@@ -2,7 +2,7 @@
$hovercolor: $surfaceContainerHigh;
$activecolor: $surfaceContainerHighest;
$rounding_unsharpen: 0.136rem;
$rounding_verysmall: 0.477rem;
$rounding_verysmall: 0.545rem;
$rounding_small: 0.818rem;
$rounding_mediumsmall: 0.955rem;
$rounding_medium: 1.159rem;
+37
View File
@@ -701,6 +701,43 @@ $skeleton-accent: mix($secondary, $onSecondary, 50%);
padding-left: 0.818rem;
}
.sidebar-chat-txtblock-think {
margin-left: -0.136rem;
padding: 0.682rem;
padding-left: 1.159rem;
}
.sidebar-chat-thinkblock {
@include small-rounding;
background-color: $layer2;
color: $onLayer3;
}
.sidebar-chat-thinkblock-icon {
@include verysmall-rounding;
background-color: $secondaryContainer;
color: $onSecondaryContainer;
min-width: 2.045rem;
min-height: 2.045rem;
}
.sidebar-chat-thinkblock-btn-arrow {
@include full-rounding;
@include icon-material;
min-width: 1.705rem;
min-height: 1.705rem;
background-color: $layer2;
&:hover,
&:focus {
background-color: $layer2Hover;
}
&:active {
background-color: $layer2Active;
}
}
.sidebar-chat-txt {
@include readingfont;
}