Skip to content

Commit bfdd868

Browse files
authored
Merge pull request #8503 from sagemathinc/jupyter-llm-8500
frontend/jupyter/llm: add "Ask a Question" and Markdown Assistant
2 parents 5c3dbbc + 70784fa commit bfdd868

File tree

17 files changed

+1156
-348
lines changed

17 files changed

+1156
-348
lines changed

src/packages/frontend/account/i18n-selector.tsx

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,16 @@ Basically a drop-down to change the language (i18n localization)
88
*/
99

1010
import { DownOutlined } from "@ant-design/icons";
11-
import { Button, Dropdown, MenuProps, Modal, Space, Tooltip } from "antd";
11+
import {
12+
Button,
13+
Dropdown,
14+
MenuProps,
15+
Modal,
16+
Select,
17+
SelectProps,
18+
Space,
19+
Tooltip,
20+
} from "antd";
1221
import { SizeType } from "antd/es/config-provider/SizeContext";
1322
import { useState } from "react";
1423
import { defineMessage, useIntl } from "react-intl";
@@ -54,6 +63,50 @@ Thank you for your patience and understanding as we work to make our application
5463
description: "Content of translation information modal",
5564
});
5665

66+
interface LanguageSelectorProps
67+
extends Omit<SelectProps, "options" | "onChange"> {
68+
value?: string;
69+
onChange?: (language: Locale) => void;
70+
}
71+
72+
/**
73+
* A reusable language selector component for translation purposes.
74+
*/
75+
export function LanguageSelector({
76+
value,
77+
onChange,
78+
...props
79+
}: LanguageSelectorProps) {
80+
const intl = useIntl();
81+
82+
let availableLocales = Object.keys(LOCALIZATIONS) as Locale[];
83+
84+
const options = availableLocales.map((locale) => {
85+
const localization = LOCALIZATIONS[locale];
86+
const other =
87+
locale === value
88+
? localization.name
89+
: intl.formatMessage(localization.trans);
90+
return {
91+
value: locale,
92+
label: `${localization.flag} ${localization.native} (${other})`,
93+
};
94+
});
95+
96+
return (
97+
<Select
98+
value={value}
99+
onChange={onChange}
100+
options={options}
101+
placeholder="Select a language..."
102+
showSearch
103+
optionFilterProp="label"
104+
popupMatchSelectWidth={false}
105+
{...props}
106+
/>
107+
);
108+
}
109+
57110
export function I18NSelector(props: Readonly<Props>) {
58111
const { isWide = true, size, confirm = false } = props;
59112

src/packages/frontend/components/raw-prompt.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { CSS } from "@cocalc/frontend/app-framework";
22
import { useBottomScroller } from "@cocalc/frontend/app-framework/use-bottom-scroller";
3-
import { COLORS } from "@cocalc/util/theme";
4-
import StaticMarkdown from "@cocalc/frontend/editors/slate/static-markdown";
53
import { Paragraph } from "@cocalc/frontend/components";
4+
import StaticMarkdown from "@cocalc/frontend/editors/slate/static-markdown";
5+
import { COLORS } from "@cocalc/util/theme";
66

77
const STYLE = {
88
border: "1px solid lightgrey",
@@ -21,18 +21,20 @@ interface Props {
2121
input: React.JSX.Element | string;
2222
style?: CSS;
2323
scrollBottom?: boolean;
24+
rawText?: boolean;
2425
}
2526

2627
export function RawPrompt({
2728
input,
2829
style: style0,
2930
scrollBottom = false,
31+
rawText = false,
3032
}: Props) {
3133
const ref = useBottomScroller(scrollBottom, input);
3234
const style = { ...STYLE, ...style0 };
33-
if (typeof input == "string") {
35+
if (typeof input == "string" && !rawText) {
3436
// this looks so much nicer; I realize it doesn't implement scrollBottom.
35-
// But just dropping the input as plain text like below just seems
37+
// But just dropping the input as plain text like below just seems
3638
// utterly broken!
3739
return <StaticMarkdown style={style} value={input} />;
3840
} else {

src/packages/frontend/cspell.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,18 @@
33
"language": "en",
44
"enabled": true,
55
"words": [
6+
"ANOVA",
7+
"ARIMA",
68
"autograde",
79
"autograding",
810
"Bioconductor",
11+
"bioinformatics",
912
"CoCalc",
1013
"conat",
1114
"dplyr",
1215
"dstream",
16+
"formatjs",
17+
"geospatial",
1318
"ggplot",
1419
"ipython",
1520
"ipywidgets",
@@ -35,14 +40,16 @@
3540
"sagetex",
3641
"sagews",
3742
"scikit",
43+
"simplelocalize",
44+
"sklearn",
3845
"SocketIO",
3946
"statsmodels",
4047
"syncdb",
4148
"syncdoc",
42-
"syncdoc",
4349
"syncstring",
4450
"synctable",
4551
"synctables",
52+
"tidymodels",
4653
"tidyverse",
4754
"timetravel",
4855
"tolerations",
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// number of characters we send to an LLM for context
2-
export const CUTOFF = 3000;
2+
export const CUTOFF = 5000;
33

44
// this came from ./create-chat, but for all frame types
55
export const AI_ASSIST_TAG = "code-editor";

src/packages/frontend/i18n/bin/common.sh

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,31 @@ check_api_key() {
66
exit 1
77
fi
88
}
9+
10+
# Execute a function for each language, optionally in parallel
11+
# Usage: run_for_each_lang <function_name>
12+
run_for_each_lang() {
13+
local func_name="$1"
14+
15+
if [ -z "$func_name" ]; then
16+
echo "Error: function name is required" >&2
17+
exit 1
18+
fi
19+
20+
start_time=$(date +%s)
21+
22+
if command -v parallel &>/dev/null; then
23+
echo "Parallel is installed. Running $func_name in parallel."
24+
export -f "$func_name"
25+
echo "$LANGS" | tr ' ' '\n' | parallel -j8 --delay 0.1 --will-cite "$func_name"
26+
else
27+
echo "Parallel is not installed. Running $func_name sequentially."
28+
for L in $LANGS; do
29+
"$func_name" "$L"
30+
done
31+
fi
32+
33+
end_time=$(date +%s)
34+
execution_time=$((end_time - start_time))
35+
echo "$func_name completed in ${execution_time} seconds."
36+
}

src/packages/frontend/i18n/bin/compile.sh

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@
44
# It was necessary to write a custom formatter (see formatter.js) – not clear why, but it works. It's just a trivial mapping.
55
# "--ast" this is the main point of compiling: we use ICU messages, which no longer need to be parsed each time.
66
# This compile step is called by the `pnpm build` step as well, hence there is no need to keep the compiled files in the sources.
7-
for L in $LANGS; do
7+
8+
# Each language is compiled into a separate file – this allows for dynamic imports.
9+
compile() {
10+
local lang="$1"
11+
echo "compiling '$lang'"
812
pnpm exec formatjs compile \
913
--ast \
1014
--format i18n/formatter.js \
11-
--out-file ./i18n/trans/$L.compiled.json \
12-
./i18n/trans/$L.json
13-
done
15+
--out-file ./i18n/trans/$lang.compiled.json \
16+
./i18n/trans/$lang.json
17+
}
18+
19+
run_for_each_lang compile

src/packages/frontend/i18n/bin/download.sh

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
check_api_key
55

6-
# Each language is downloaded into a spearate file and compiled – this allows for dynamic imports.
6+
# Each language is downloaded into a separate file and compiled – this allows for dynamic imports.
77
download() {
88
local lang="$1"
99
echo "calling download '$lang'"
@@ -14,13 +14,4 @@ download() {
1414
--languageKey="$lang"
1515
}
1616

17-
if command -v parallel &>/dev/null; then
18-
echo "Parallel is installed. Running downloads in parallel."
19-
export -f download
20-
echo "$LANGS" | tr ' ' '\n' | parallel -j8 --delay 0.1 --will-cite download
21-
else
22-
echo "Parallel is not installed. Running downloads sequentially."
23-
for L in $LANGS; do
24-
download "$L"
25-
done
26-
fi
17+
run_for_each_lang download

src/packages/frontend/jupyter/_jupyter.sass

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
.cocalc-jupyter-insert-cell
6969
margin-bottom: 10px
7070
height: 8px
71-
margin-top: 8px
71+
margin-top: 10px
7272
cursor: pointer
7373
text-align: center
7474
&:hover
@@ -111,4 +111,4 @@
111111
// %matplotlib widgets
112112
// from pylab import plot; plot([1,2],[3,4])
113113
cocalc-lumino-adapter
114-
position: relative
114+
position: relative

src/packages/frontend/jupyter/cell-buttonbar.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import {
3131
RUN_ALL_CELLS_ABOVE_ICON,
3232
RUN_ALL_CELLS_BELOW_ICON,
3333
} from "./consts";
34-
import { LLMCellTool } from "./llm";
34+
import { LLMCellTool } from "./llm/cell-tool";
3535

3636
interface Props {
3737
id: string;
@@ -192,8 +192,15 @@ export const CellButtonBar: React.FC<Props> = React.memo(
192192
}
193193

194194
function renderCodeBarLLMButtons() {
195-
if (!isCodeCell || !llmTools || !haveLLMCellTools || is_readonly) return;
196-
return <LLMCellTool id={id} actions={actions} llmTools={llmTools} />;
195+
if (!llmTools || !haveLLMCellTools || is_readonly) return;
196+
return (
197+
<LLMCellTool
198+
id={id}
199+
actions={actions}
200+
llmTools={llmTools}
201+
cellType={isCodeCell ? "code" : "markdown"}
202+
/>
203+
);
197204
}
198205

199206
function renderCodeBarFormatButton() {
@@ -204,7 +211,7 @@ export const CellButtonBar: React.FC<Props> = React.memo(
204211
return (
205212
<Tooltip
206213
title={intl.formatMessage({
207-
id: "jupyter.cell-buttonbr.format-button.tooltip",
214+
id: "jupyter.cell-buttonbar.format-button.tooltip",
208215
defaultMessage: "Format this code to look nice",
209216
description: "Code cell in a Jupyter Notebook",
210217
})}
@@ -230,7 +237,7 @@ export const CellButtonBar: React.FC<Props> = React.memo(
230237
>
231238
<Icon name={formatting ? "spinner" : "sitemap"} spin={formatting} />{" "}
232239
<FormattedMessage
233-
id="jupyter.cell-buttonbr.format-button.label"
240+
id="jupyter.cell-buttonbar.format-button.label"
234241
defaultMessage={"Format"}
235242
description={"Code cell in a Jupyter Notebook"}
236243
/>

0 commit comments

Comments
 (0)