Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
473 changes: 397 additions & 76 deletions sippy-ng/package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions sippy-ng/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.8.3",
"@types/js-yaml": "^4.0.9",
"chart.js": "^3.5.1",
"chartjs-plugin-annotation": "^1.0.2",
"chartjs-plugin-trendline": "^0.1.1",
Expand All @@ -28,6 +29,8 @@
"date-fns-tz": "^1.1.6",
"eventemitter3": "^5.0.1",
"idb-keyval": "^6.2.2",
"js-yaml": "^4.1.0",
"nunjucks": "^3.2.4",
"plotly.js": "^3.1.1",
"prop-types": "^15.8.1",
"query-string": "^4.3.4",
Expand All @@ -40,6 +43,7 @@
"react-markdown": "^8.0.7",
"react-plotly.js": "^2.6.0",
"react-router-dom": "^6.26.0",
"react-syntax-highlighter": "^16.1.0",
"remark-gfm": "^3.0.1",
"timelines-chart": "^2.12.1",
"universal-cookie": "^7.2.1",
Expand Down
27 changes: 26 additions & 1 deletion sippy-ng/src/chat/ChatHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Typography,
} from '@mui/material'
import {
Code as CodeIcon,
ExpandMore as ExpandMoreIcon,
Help as HelpIcon,
Fullscreen as MaximizeIcon,
Expand All @@ -17,13 +18,15 @@ import { makeStyles } from '@mui/styles'
import {
useConnectionState,
usePageContextForChat,
usePrompts,
useSessionState,
useSettings,
useShareActions,
useShareState,
} from './store/useChatStore'
import PromptManagerModal from './PromptManagerModal'
import PropTypes from 'prop-types'
import React from 'react'
import React, { useState } from 'react'
import SessionManager from './SessionDropdown'

const useStyles = makeStyles((theme) => ({
Expand Down Expand Up @@ -103,10 +106,13 @@ export default function ChatHeader({
const { shareConversation } = useShareActions()
const { setSettingsOpen } = useSettings()
const { pageContext } = usePageContextForChat()
const { localPrompts } = usePrompts()

const messages = activeSession?.messages || []
const hasMessages = messages.length > 0

const [promptManagerOpen, setPromptManagerOpen] = useState(false)

const handleHelp = () => {
window.open(
'https://source.redhat.com/departments/products_and_global_engineering/openshift_development/openshift_wiki/sippy_chat_user_guide',
Expand Down Expand Up @@ -155,6 +161,20 @@ export default function ChatHeader({
</span>
</Tooltip>

<Tooltip
title={`Manage Custom Prompts${
localPrompts.length > 0 ? ` (${localPrompts.length})` : ''
}`}
>
<IconButton
size="small"
onClick={() => setPromptManagerOpen(true)}
data-tour="prompt-manager-button"
>
<CodeIcon />
</IconButton>
</Tooltip>

<Tooltip title="Help">
<IconButton size="small" onClick={handleHelp} data-tour="help-button">
<HelpIcon />
Expand Down Expand Up @@ -187,6 +207,11 @@ export default function ChatHeader({
</>
)}
</div>

<PromptManagerModal
open={promptManagerOpen}
onClose={() => setPromptManagerOpen(false)}
/>
</div>
)
}
Expand Down
85 changes: 75 additions & 10 deletions sippy-ng/src/chat/ChatInput.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import {
Chip,
CircularProgress,
IconButton,
Paper,
TextField,
Tooltip,
} from '@mui/material'
import {
AddCircleOutline as AddCircleOutlineIcon,
Code as CodeIcon,
Masks as MasksIcon,
PlayArrow as PlayArrowIcon,
Refresh as RefreshIcon,
Send as SendIcon,
Stop as StopIcon,
} from '@mui/icons-material'
import {
Chip,
CircularProgress,
IconButton,
Paper,
TextField,
Tooltip,
} from '@mui/material'
import { CONNECTION_STATES } from './store/webSocketSlice'
import { extractYAMLFromText } from './promptSchema'
import { humanize, validateMessage } from './chatUtils'
import { makeStyles } from '@mui/styles'
import {
Expand All @@ -24,6 +26,8 @@ import {
useSettings,
useWebSocketActions,
} from './store/useChatStore'
import CreatePromptDialog from './CreatePromptDialog'
import PromptEditor from './PromptEditor'
import PropTypes from 'prop-types'
import React, { useEffect, useRef, useState } from 'react'
import SlashCommandModal from './SlashCommandModal'
Expand Down Expand Up @@ -121,6 +125,11 @@ export default function ChatInput({
const [commandMenuAnchor, setCommandMenuAnchor] = useState(null)
const slashNavigationRef = useRef(null)

// Custom prompt creation state
const [createPromptDialogOpen, setCreatePromptDialogOpen] = useState(false)
const [promptEditorOpen, setPromptEditorOpen] = useState(false)
const [promptEditorInitialYAML, setPromptEditorInitialYAML] = useState(null)

const { settings } = useSettings()
const { personas } = usePersonas()
const { prompts, renderPrompt } = usePrompts()
Expand Down Expand Up @@ -189,6 +198,34 @@ export default function ChatInput({
setCommandMenuAnchor(null)
}

const handleCreatePromptClick = () => {
setCreatePromptDialogOpen(true)
}

const handleYAMLGenerated = (aiResponse) => {
console.log('ChatInput: Received AI response, length:', aiResponse.length)

// Extract YAML from the AI response
const yamlBlocks = extractYAMLFromText(aiResponse)
const yamlContent = yamlBlocks.length > 0 ? yamlBlocks[0] : aiResponse

console.log('ChatInput: Extracted YAML length:', yamlContent.length)
console.log('ChatInput: First 500 chars:', yamlContent.substring(0, 500))
console.log(
'ChatInput: Last 500 chars:',
yamlContent.substring(Math.max(0, yamlContent.length - 500))
)

// Open the prompt editor with the generated YAML
setPromptEditorInitialYAML(yamlContent)
setPromptEditorOpen(true)
}

const handlePromptEditorClose = () => {
setPromptEditorOpen(false)
setPromptEditorInitialYAML(null)
}

const handleSendMessage = () => {
const validation = validateMessage(message)

Expand Down Expand Up @@ -307,8 +344,8 @@ export default function ChatInput({

const getCharacterCountClass = () => {
const length = message.length
if (length > 9000) return 'error'
if (length > 8000) return 'warning'
if (length > 90000) return 'error'
if (length > 80000) return 'warning'
return ''
Comment on lines +347 to 349
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix character limit display inconsistency.

The validation logic checks for 90,000/80,000 character limits, but the UI displays a maximum of 10,000. According to the PR description, the message limit was increased to 100K, so the display should reflect the actual limits.

Apply this diff to fix the display:

           className={`${classes.characterCount} ${getCharacterCountClass()}`}
         >
-          {message.length}/10000
+          {message.length}/100000
         </span>

Also applies to: 421-421

🤖 Prompt for AI Agents
In sippy-ng/src/chat/ChatInput.js around lines 347-349 (and also at line 421),
the validation uses 90,000/80,000 thresholds but the UI displays a 10,000 max;
update the UI display to reflect the actual 100,000 message limit and matching
warning/error thresholds. Replace hard-coded display values with constants
(e.g., MAX_MESSAGE_LEN = 100000, WARNING_THRESHOLD = 80000, ERROR_THRESHOLD =
90000) or update the displayed numbers to 100000/80000/90000, and ensure both
locations use the same constants so validation and display remain consistent.

}

Expand Down Expand Up @@ -475,6 +512,20 @@ export default function ChatInput({
</span>
</Tooltip>

{/* Create custom prompt button */}
<Tooltip title="Create custom prompt with AI">
<span>
<IconButton
className={classes.commandMenuButton}
onClick={handleCreatePromptClick}
disabled={disabled || isTyping}
color="secondary"
>
<AddCircleOutlineIcon />
</IconButton>
</span>
</Tooltip>

<Tooltip
title={
canSend
Expand Down Expand Up @@ -517,6 +568,20 @@ export default function ChatInput({
onSubmit={handleModalSubmit}
disabled={isTyping}
/>

{/* Create prompt dialog */}
<CreatePromptDialog
open={createPromptDialogOpen}
onClose={() => setCreatePromptDialogOpen(false)}
onYAMLGenerated={handleYAMLGenerated}
/>

{/* Prompt editor */}
<PromptEditor
open={promptEditorOpen}
onClose={handlePromptEditorClose}
initialYAML={promptEditorInitialYAML}
/>
</Paper>
)
}
Expand Down
64 changes: 64 additions & 0 deletions sippy-ng/src/chat/ChatSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
import {
VerticalAlignBottom as AutoScrollIcon,
Close as CloseIcon,
Code as CodeIcon,
Delete as DeleteIcon,
Info as InfoIcon,
Masks as MasksIcon,
Expand All @@ -37,10 +38,12 @@ import { makeStyles } from '@mui/styles'
import {
useConnectionState,
usePersonas,
usePrompts,
useSessionActions,
useSessionState,
useSettings,
} from './store/useChatStore'
import PromptManagerModal from './PromptManagerModal'
import PropTypes from 'prop-types'
import React, { useCallback, useEffect, useState } from 'react'

Expand Down Expand Up @@ -109,6 +112,7 @@ export default function ChatSettings({ onClearMessages, onReconnect }) {
const { connectionState } = useConnectionState()
const { personas, personasLoading, personasError, loadPersonas } =
usePersonas()
const { localPrompts } = usePrompts()
const { sessions, activeSessionId } = useSessionState()
const { clearAllSessions, clearOldSessions } = useSessionActions()

Expand All @@ -122,6 +126,9 @@ export default function ChatSettings({ onClearMessages, onReconnect }) {
})
const [storageLoading, setStorageLoading] = useState(false)

// Prompt manager
const [promptManagerOpen, setPromptManagerOpen] = useState(false)

useEffect(() => {
if (personas.length === 0 && !personasLoading) {
loadPersonas()
Expand Down Expand Up @@ -475,6 +482,57 @@ export default function ChatSettings({ onClearMessages, onReconnect }) {

<Divider />

{/* Custom Prompts */}
<div className={classes.section}>
<Typography variant="subtitle2" className={classes.sectionTitle}>
<Box display="flex" alignItems="center" gap={0.5}>
<CodeIcon fontSize="small" />
Custom Prompts
<Tooltip
title="Create and manage your own AI prompt templates stored locally"
placement="top"
>
<InfoIcon
fontSize="small"
sx={{ color: 'text.secondary', cursor: 'help' }}
/>
</Tooltip>
</Box>
</Typography>

<Box
sx={{
mb: 2,
p: 1.5,
backgroundColor: (theme) =>
theme.palette.mode === 'dark'
? 'rgba(255, 255, 255, 0.05)'
: 'rgba(0, 0, 0, 0.02)',
borderRadius: 1,
textAlign: 'center',
}}
>
<Typography variant="body2" color="textSecondary" gutterBottom>
{localPrompts.length === 0
? 'No custom prompts yet'
: `${localPrompts.length} custom prompt${
localPrompts.length !== 1 ? 's' : ''
}`}
</Typography>
</Box>

<Button
variant="outlined"
startIcon={<CodeIcon />}
onClick={() => setPromptManagerOpen(true)}
fullWidth
>
Manage Custom Prompts
</Button>
</div>

<Divider />

{/* Tour Management */}
<div className={classes.section}>
<Typography variant="subtitle2" className={classes.sectionTitle}>
Expand All @@ -500,6 +558,12 @@ export default function ChatSettings({ onClearMessages, onReconnect }) {
</Typography>
)}
</div>

{/* Prompt Manager Modal */}
<PromptManagerModal
open={promptManagerOpen}
onClose={() => setPromptManagerOpen(false)}
/>
</Drawer>
)
}
Expand Down
Loading