Skip to content

Commit a550e97

Browse files
Drop EventSource for SSE
* Replaced with a standard fetch() (as IndicoAxios isn't as smooth) * Split main stylesheet across components * Add i18n to prompt management components * Use `load_default` in webargs * Use indico-url imports
1 parent 7a051ab commit a550e97

12 files changed

+276
-143
lines changed

ai_summary/indico_ai_summary/blueprint.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@
1414

1515
blueprint.add_url_rule('/manage-category-prompts/<int:category_id>', 'manage_category_prompts',
1616
RHManageCategoryPrompts, methods=('GET', 'POST'))
17-
blueprint.add_url_rule('/summarize-event/<int:event_id>', 'summarize_event', SummarizeEvent, methods=('GET', 'POST'))
17+
blueprint.add_url_rule('/summarize-event/<int:event_id>', 'summarize_event', SummarizeEvent, methods=('POST',))

ai_summary/indico_ai_summary/client/CategoryManagePrompts.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
// them and/or modify them under the terms of the MIT License;
66
// see the LICENSE file for more details.
77

8+
import manageCategoryPrompts from 'indico-url:plugin_ai_summary.manage_category_prompts';
9+
810
import _ from 'lodash';
911
import React from 'react';
1012
import ReactDOM from 'react-dom';
@@ -18,10 +20,8 @@ import {FinalPromptManagerField} from './components/PromptManagerField';
1820

1921
export default function CategoryManagePrompts({categoryId, prompts: predefinedPrompts}) {
2022
const onSubmit = async ({prompts}, form) => {
21-
// TODO: url import
22-
const url = `/plugin/ai-summary/manage-category-prompts/${categoryId}`;
2323
try {
24-
await indicoAxios.post(url, {prompts});
24+
await indicoAxios.post(manageCategoryPrompts({category_id: categoryId}), {prompts});
2525
form.initialize({prompts});
2626
} catch (error) {
2727
handleSubmitError(error);

ai_summary/indico_ai_summary/client/components/ActionButtons.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import React, {useState, useEffect} from 'react';
99
import {Button, ButtonGroup, Icon, Popup} from 'semantic-ui-react';
1010
import {Translate} from 'indico/react/i18n';
1111

12-
import '../styles/ind_summarize_button.module.scss';
12+
import './ActionButtons.module.scss';
1313

1414
export default function ActionButtons({loading, error, summaryHtml, saving, onSave, onRetry}) {
1515

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// This file is part of the Indico plugins.
2+
// Copyright (C) 2002 - 2025 CERN
3+
//
4+
// The Indico plugins are free software; you can redistribute
5+
// them and/or modify them under the terms of the MIT License;
6+
// see the LICENSE file for more details.
7+
8+
@use 'base/palette' as *;
9+
10+
.action-buttons {
11+
display: flex;
12+
justify-content: space-between;
13+
align-items: baseline;
14+
15+
.llm-info {
16+
color: $dark-gray;
17+
}
18+
}
19+
20+
.action-buttons-container {
21+
display: flex;
22+
align-items: baseline;
23+
gap: 0;
24+
25+
.save-button {
26+
margin-left: 10px;
27+
}
28+
}

ai_summary/indico_ai_summary/client/components/PromptManagerField.jsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@ import _ from 'lodash';
99
import React, {useCallback, useMemo, useState} from 'react';
1010
import ReactDOM from 'react-dom';
1111
import {Form, TextArea, Button, Input, Card, Icon} from 'semantic-ui-react';
12+
import {Translate} from 'indico/react/i18n';
1213

1314
import {FinalField} from 'indico/react/forms';
1415

16+
import './PromptManagerField.module.scss';
17+
1518
export function FinalPromptManagerField({submitBtn}) {
1619
return (
1720
<FinalField
@@ -52,9 +55,9 @@ export default function PromptManagerField({value, onChange, submitBtn}) {
5255
onChangeName={name => updateName(idx, name)}
5356
/>
5457
))}
55-
<div style={{display: 'flex', justifyContent: 'end'}}>
58+
<div styleName="add-prompt-buttons">
5659
<Button icon type="button" labelPosition="right" onClick={addPrompt}>
57-
Add Prompt
60+
<Translate>Add Prompt</Translate>
5861
<Icon name="plus" />
5962
</Button>
6063
{submitBtn}
@@ -67,24 +70,24 @@ function PromptField({name, text, onChangeName, onChangeText, onRemove}) {
6770
return (
6871
<Card fluid>
6972
<Card.Content>
70-
<div style={{display: 'flex', justifyContent: 'end', marginBottom: '-1em'}}>
73+
<div styleName="delete-prompt-button">
7174
<Button icon type="button" compact onClick={onRemove}>
7275
<Icon name="trash alternate outline" />
7376
</Button>
7477
</div>
7578
<Form.Field>
76-
<label>Prompt Name</label>
79+
<Translate as="label">Prompt Name</Translate>
7780
<Input
7881
value={name}
79-
placeholder="e.g. Summarizer..."
82+
placeholder={Translate.string('e.g. Summarizer...')}
8083
onChange={(_, {value: v}) => onChangeName(v)}
8184
/>
8285
</Form.Field>
8386
<Form.Field>
84-
<label>Prompt Text</label>
87+
<Translate as="label">Prompt Text</Translate>
8588
<TextArea
8689
value={text}
87-
placeholder="Enter your LLM prompt here..."
90+
placeholder={Translate.string('Enter your prompt here...')}
8891
onChange={(_, {value: v}) => onChangeText(v)}
8992
rows={10}
9093
/>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// This file is part of the Indico plugins.
2+
// Copyright (C) 2002 - 2025 CERN
3+
//
4+
// The Indico plugins are free software; you can redistribute
5+
// them and/or modify them under the terms of the MIT License;
6+
// see the LICENSE file for more details.
7+
8+
.add-prompt-buttons {
9+
display: flex;
10+
justify-content: end;
11+
}
12+
13+
.delete-prompt-button {
14+
display: flex;
15+
justify-content: end;
16+
margin-bottom: -1em;
17+
}

ai_summary/indico_ai_summary/client/components/SummaryPreview.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import React, {useRef, useEffect} from 'react';
1111
import {Button, Segment, Header, HeaderSubheader, Icon, Dimmer, Image, Card} from 'semantic-ui-react';
1212
import {Translate} from 'indico/react/i18n';
1313

14-
import '../styles/ind_summarize_button.module.scss';
14+
import './SummaryPreview.module.scss';
15+
import './ActionButtons.module.scss';
16+
1517
import ActionButtons from './ActionButtons';
1618

1719
export default function SummaryPreview({loading, error, summaryHtml, saving, onSave, streamResponse, onRetry, llmInfo}) {

ai_summary/indico_ai_summary/client/styles/ind_summarize_button.module.scss renamed to ai_summary/indico_ai_summary/client/components/SummaryPreview.module.scss

Lines changed: 2 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,6 @@
55
// them and/or modify them under the terms of the MIT License;
66
// see the LICENSE file for more details.
77

8-
@use 'base/palette' as *;
9-
10-
.column-divider {
11-
box-shadow: none !important;
12-
display: flex !important;
13-
flex-direction: column;
14-
border-left: 1px solid $raincloud-gray;
15-
}
16-
178
.preview-card-wrapper {
189
flex-grow: 1;
1910
margin-top: 0 !important;
@@ -27,33 +18,13 @@
2718
ul li {
2819
margin-left: 1.5em;
2920
}
21+
3022
h1, h2, h3 {
3123
font-size: 1.5em;
3224
}
3325
}
3426
}
3527

36-
.completion-status {
37-
38-
margin-left: 5px !important;
39-
40-
.modal-spinner {
41-
// SUI modal loader workaround (https://github.com/Semantic-Org/Semantic-UI-React/issues/3133)
42-
43-
&::before {
44-
width: 18px !important;
45-
height: 18px !important;
46-
border-color: rgba(0,0,0,.1) !important;
47-
}
48-
49-
&::after {
50-
width: 18px !important;
51-
height: 18px !important;
52-
border-color: #767676 transparent transparent !important;
53-
}
54-
}
55-
}
56-
5728
.no-preview-placeholder {
5829
margin-top: 0 !important;
5930
height: 52.5vh;
@@ -85,7 +56,7 @@
8556
display: none;
8657
}
8758

88-
// Hide scrollbar but allow scrolling
59+
// Hide scrollbar when streaming back the generated summary
8960
.scrollbar-hidden {
9061
scrollbar-width: none;
9162
-ms-overflow-style: none;
@@ -131,43 +102,3 @@
131102
text-align: center;
132103
}
133104
}
134-
135-
.placeholder-no-summary{
136-
display: flex;
137-
align-items: center;
138-
justify-content: center;
139-
text-align: center;
140-
color: rgba(0,0,0,0.5);
141-
margin-top: 8em;
142-
font-style: italic;
143-
}
144-
145-
.summarize-button-menu {
146-
display: flex;
147-
flex-direction: row;
148-
padding-bottom: 0;
149-
150-
img {
151-
margin-right: 5px;
152-
}
153-
}
154-
155-
.action-buttons {
156-
display: flex;
157-
justify-content: space-between;
158-
align-items: baseline;
159-
160-
.llm-info {
161-
color: $dark-gray;
162-
}
163-
}
164-
165-
.action-buttons-container {
166-
display: flex;
167-
align-items: baseline;
168-
gap: 0;
169-
170-
.save-button {
171-
margin-left: 10px !important;
172-
}
173-
}

ai_summary/indico_ai_summary/client/components/ind_summarize_button.jsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@
1414

1515
import staticURL from 'indico-url:plugin_ai_summary.static';
1616

17-
import React, {useState, useEffect, useContext} from 'react';
17+
import React, {useState, useEffect} from 'react';
1818
import ReactDOM from 'react-dom';
1919
import {Modal, Button, Form, Grid, GridRow, GridColumn, Header, Icon, Loader, Popup} from 'semantic-ui-react';
2020
import {Translate} from 'indico/react/i18n';
2121
import {handleAxiosError} from 'indico/utils/axios';
2222
import {streamSummary, fetchSummary, fetchEventNote, saveSummaryToEvent} from '../services/summarize';
2323
import {PromptControls, PromptEditor} from './PromptSelector';
2424
import SummaryPreview from './SummaryPreview';
25-
import '../styles/ind_summarize_button.module.scss';
25+
import './ind_summarize_button.module.scss';
2626

2727
function SummarizeButton({categoryId, eventId, storedPrompts, streamResponse, llmInfo}) {
2828
const [selectedPromptIndex, setSelectedPromptIndex] = useState(0);
@@ -40,7 +40,7 @@ function SummarizeButton({categoryId, eventId, storedPrompts, streamResponse, ll
4040
// Close any existing stream first
4141
if (streamCtl) {
4242
try {
43-
streamCtl.close();
43+
streamCtl.abort();
4444
} catch {}
4545
setStreamCtl(null);
4646
}
@@ -51,7 +51,7 @@ function SummarizeButton({categoryId, eventId, storedPrompts, streamResponse, ll
5151

5252
if (streamResponse) {
5353
setStreamStopped(false);
54-
// Streaming via SSE
54+
// Streaming via indicoAxios
5555
const ctl = streamSummary(eventId, selectedPrompt.text, {
5656
onChunk: html => {
5757
// Replace each time with server snapshot
@@ -92,7 +92,7 @@ function SummarizeButton({categoryId, eventId, storedPrompts, streamResponse, ll
9292
const stopStreaming = () => {
9393
if (streamCtl) {
9494
try {
95-
streamCtl.close();
95+
streamCtl.abort();
9696
} catch {}
9797
setStreamCtl(null);
9898
}
@@ -104,7 +104,7 @@ function SummarizeButton({categoryId, eventId, storedPrompts, streamResponse, ll
104104
useEffect(() => () => {
105105
if (streamCtl) {
106106
try {
107-
streamCtl.close();
107+
streamCtl.abort();
108108
} catch {}
109109
}
110110
}, [streamCtl]);
@@ -189,7 +189,7 @@ function SummarizeButton({categoryId, eventId, storedPrompts, streamResponse, ll
189189
// Ensure any active stream is closed when modal is closed
190190
if (streamCtl) {
191191
try {
192-
streamCtl.close();
192+
streamCtl.abort();
193193
} catch {}
194194
setStreamCtl(null);
195195
}
@@ -244,7 +244,11 @@ function SummarizeButton({categoryId, eventId, storedPrompts, streamResponse, ll
244244
/>
245245
)}
246246
{!loading && !error && !streamStopped && summaryHtml && (
247-
<Icon name="check circle outline" color="green" />
247+
<Popup
248+
trigger={<Icon name="check circle outline" color="green" />}
249+
content={Translate.string('Response complete')}
250+
position="top center"
251+
/>
248252
)}
249253
</span>
250254
</Header>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// This file is part of the Indico plugins.
2+
// Copyright (C) 2002 - 2025 CERN
3+
//
4+
// The Indico plugins are free software; you can redistribute
5+
// them and/or modify them under the terms of the MIT License;
6+
// see the LICENSE file for more details.
7+
8+
@use 'base/palette' as *;
9+
10+
.column-divider {
11+
box-shadow: none !important;
12+
display: flex !important;
13+
flex-direction: column;
14+
border-left: 1px solid $raincloud-gray;
15+
}
16+
17+
.completion-status {
18+
margin-left: 5px !important;
19+
20+
.modal-spinner {
21+
// SUI modal loader workaround (https://github.com/Semantic-Org/Semantic-UI-React/issues/3133)
22+
23+
&::before {
24+
width: 18px !important;
25+
height: 18px !important;
26+
border-color: rgba(0,0,0,.1) !important;
27+
}
28+
29+
&::after {
30+
width: 18px !important;
31+
height: 18px !important;
32+
border-color: #767676 transparent transparent !important;
33+
}
34+
}
35+
}
36+
37+
.summarize-button-menu {
38+
display: flex;
39+
flex-direction: row;
40+
padding-bottom: 0;
41+
42+
img {
43+
margin-right: 5px;
44+
}
45+
}

0 commit comments

Comments
 (0)