Making UI progress; seeding lkml RAG
This commit is contained in:
parent
1c56814a92
commit
d815842f0e
@ -14,9 +14,8 @@ div {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
border: 1px solid red;
|
max-width: 800px;
|
||||||
max-width: 800px;
|
margin: 0 auto;
|
||||||
margin: 0 auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.Controls {
|
.Controls {
|
||||||
@ -30,10 +29,16 @@ div {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
overflow-x: visible;
|
overflow-x: visible;
|
||||||
min-width: 10rem;
|
min-width: 10rem;
|
||||||
|
width: 100%;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
border: 1px solid magenta;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.Controls {
|
||||||
|
width: 600px; /* or whatever you prefer for a desktop */
|
||||||
|
max-width: 80vw; /* Optional: Prevent it from taking up too much space */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.Conversation {
|
.Conversation {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -54,7 +59,7 @@ div {
|
|||||||
margin-left: 1rem;
|
margin-left: 1rem;
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
min-width: 80%;
|
min-width: 80%;
|
||||||
max-width: 80%;
|
max-width: 80%;
|
||||||
justify-self: right;
|
justify-self: right;
|
||||||
display: flex;
|
display: flex;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
@ -62,7 +67,7 @@ div {
|
|||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: self-end;
|
align-items: self-end;
|
||||||
align-self: end;
|
align-self: end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.assistant-message {
|
.assistant-message {
|
||||||
@ -95,6 +100,7 @@ div {
|
|||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
padding: 0.125rem;
|
padding: 0.125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reduce general whitespace in markdown content */
|
/* Reduce general whitespace in markdown content */
|
||||||
.markdown-content p {
|
.markdown-content p {
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import FormGroup from '@mui/material/FormGroup';
|
import FormGroup from '@mui/material/FormGroup';
|
||||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||||
import Switch from '@mui/material/Switch';
|
import Switch from '@mui/material/Switch';
|
||||||
|
import Divider from '@mui/material/Divider';
|
||||||
import Snackbar, { SnackbarCloseReason } from '@mui/material/Snackbar';
|
import Snackbar, { SnackbarCloseReason } from '@mui/material/Snackbar';
|
||||||
import Alert from '@mui/material/Alert';
|
import Alert from '@mui/material/Alert';
|
||||||
import TextField from '@mui/material/TextField';
|
import TextField from '@mui/material/TextField';
|
||||||
@ -46,7 +47,15 @@ const getConnectionBase = (loc: any): string => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Tool = {
|
type Tool = {
|
||||||
label: string,
|
type: string,
|
||||||
|
function?: {
|
||||||
|
name: string,
|
||||||
|
description: string,
|
||||||
|
parameters?: any,
|
||||||
|
returns?: any
|
||||||
|
},
|
||||||
|
name?: string,
|
||||||
|
description?: string,
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -56,11 +65,14 @@ interface ControlsParams {
|
|||||||
sessionId: string,
|
sessionId: string,
|
||||||
connectionBase: string,
|
connectionBase: string,
|
||||||
setSnack: (snackMessage: string, snackSeverity?: SeverityType) => void
|
setSnack: (snackMessage: string, snackSeverity?: SeverityType) => void
|
||||||
|
onClearHistory: () => void
|
||||||
};
|
};
|
||||||
|
|
||||||
const Controls = ({ sessionId, connectionBase, setSnack }: ControlsParams) => {
|
const Controls = ({ sessionId, connectionBase, setSnack, onClearHistory }: ControlsParams) => {
|
||||||
const [systemPrompt, setSystemPrompt] = useState<string>("");
|
const [systemPrompt, setSystemPrompt] = useState<string>("");
|
||||||
const [editSystemPrompt, setEditSystemPrompt] = useState<string>(systemPrompt);
|
const [editSystemPrompt, setEditSystemPrompt] = useState<string>(systemPrompt);
|
||||||
|
const [tools, setTools] = useState<Tool[]>([]);
|
||||||
|
const [rags, setRags] = useState<Tool[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (systemPrompt !== "") {
|
if (systemPrompt !== "") {
|
||||||
@ -76,34 +88,99 @@ const Controls = ({ sessionId, connectionBase, setSnack }: ControlsParams) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
setSystemPrompt(data["system-prompt"]);
|
const systemPrompt = data["system-prompt"].trim();
|
||||||
setEditSystemPrompt(data["system-prompt"]);
|
setSystemPrompt(systemPrompt);
|
||||||
|
setEditSystemPrompt(systemPrompt);
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchSystemPrompt();
|
fetchSystemPrompt();
|
||||||
}, [sessionId, systemPrompt, setSystemPrompt, setEditSystemPrompt, connectionBase]);
|
}, [sessionId, systemPrompt, setSystemPrompt, setEditSystemPrompt, connectionBase]);
|
||||||
|
|
||||||
const tools: Tool[] = [{
|
useEffect(() => {
|
||||||
label: "get_stock_price",
|
if (tools.length) {
|
||||||
enabled: true,
|
return;
|
||||||
}, {
|
}
|
||||||
label: "get_weather",
|
const fetchTools = async () => {
|
||||||
enabled: true
|
try {
|
||||||
}, {
|
// Make the fetch request with proper headers
|
||||||
label: "site_summary",
|
const response = await fetch(connectionBase + `/api/tools/${sessionId}`, {
|
||||||
enabled: true
|
method: 'GET',
|
||||||
}];
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw Error();
|
||||||
|
}
|
||||||
|
const tools = await response.json();
|
||||||
|
setTools(tools);
|
||||||
|
} catch (error: any) {
|
||||||
|
setSnack("Unable to fetch tools", "error");
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const rags: Tool[] = [{
|
fetchTools();
|
||||||
label: "RAG JPK",
|
}, [sessionId, tools, setTools, setSnack, connectionBase]);
|
||||||
enabled: false
|
|
||||||
}, {
|
|
||||||
label: "RAG LKML",
|
|
||||||
enabled: false
|
|
||||||
}];
|
|
||||||
|
|
||||||
const toggleTool = (event: any) => {
|
useEffect(() => {
|
||||||
console.log(`${event.target.value} clicked`)
|
if (rags.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fetchRags = async () => {
|
||||||
|
try {
|
||||||
|
// Make the fetch request with proper headers
|
||||||
|
const response = await fetch(connectionBase + `/api/rags/${sessionId}`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw Error();
|
||||||
|
}
|
||||||
|
const rags = await response.json();
|
||||||
|
setRags(rags);
|
||||||
|
} catch (error: any) {
|
||||||
|
setSnack("Unable to fetch RAGs", "error");
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchRags();
|
||||||
|
}, [sessionId, rags, setRags, setSnack, connectionBase]);
|
||||||
|
|
||||||
|
const toggleTool = async (type: string, index: number) => {
|
||||||
|
switch (type) {
|
||||||
|
case "rag":
|
||||||
|
setSnack("RAG backend not yet implemented", "warning");
|
||||||
|
// rags[index].enabled = !rags[index].enabled
|
||||||
|
// setRags([...rags])
|
||||||
|
break;
|
||||||
|
case "tool":
|
||||||
|
const tool = tools[index];
|
||||||
|
tool.enabled = !tool.enabled
|
||||||
|
try {
|
||||||
|
const response = await fetch(connectionBase + `/api/tools/${sessionId}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ "tool": tool?.function?.name, "enabled": tool.enabled }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const tools = await response.json();
|
||||||
|
setTools([...tools])
|
||||||
|
setSnack(`${tool?.function?.name} ${tool.enabled ? "enabled" : "disabled"}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fetch error:', error);
|
||||||
|
setSnack(`${tool?.function?.name} ${tool.enabled ? "enabling" : "disabling"} failed.`, "error");
|
||||||
|
tool.enabled = !tool.enabled
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const sendSystemPrompt = async () => {
|
const sendSystemPrompt = async () => {
|
||||||
@ -123,7 +200,6 @@ const Controls = ({ sessionId, connectionBase, setSnack }: ControlsParams) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
console.log(data);
|
|
||||||
if (data["system-prompt"] !== systemPrompt) {
|
if (data["system-prompt"] !== systemPrompt) {
|
||||||
setSystemPrompt(data["system-prompt"].trim());
|
setSystemPrompt(data["system-prompt"].trim());
|
||||||
setSnack("System prompt updated", "success");
|
setSnack("System prompt updated", "success");
|
||||||
@ -134,7 +210,7 @@ const Controls = ({ sessionId, connectionBase, setSnack }: ControlsParams) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetSystemPrompt = async () => {
|
const onResetSystemPrompt = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(connectionBase + `/api/system-prompt/${sessionId}`, {
|
const response = await fetch(connectionBase + `/api/system-prompt/${sessionId}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
@ -155,6 +231,54 @@ const Controls = ({ sessionId, connectionBase, setSnack }: ControlsParams) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onResetToDefaults = async (event: any) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(connectionBase + `/api/reset/${sessionId}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ "reset": ["rag", "tools"] }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
await response.json();
|
||||||
|
setSnack("Defaults restored", "success");
|
||||||
|
} else {
|
||||||
|
throw Error(`${{ status: response.status, message: response.statusText }}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fetch error:', error);
|
||||||
|
setSnack("Unable to restore defaults", "error");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClearContext = async (event: any) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(connectionBase + `/api/reset/${sessionId}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ "reset": ["history"] }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
await response.json();
|
||||||
|
setSnack("Chat history cleared", "success");
|
||||||
|
onClearHistory();
|
||||||
|
} else {
|
||||||
|
throw Error(`${{ status: response.status, message: response.statusText }}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fetch error:', error);
|
||||||
|
setSnack("Unable to clear chat history", "error");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleKeyPress = (event: any) => {
|
const handleKeyPress = (event: any) => {
|
||||||
if (event.key === 'Enter' && event.ctrlKey) {
|
if (event.key === 'Enter' && event.ctrlKey) {
|
||||||
switch (event.target.id) {
|
switch (event.target.id) {
|
||||||
@ -166,42 +290,10 @@ const Controls = ({ sessionId, connectionBase, setSnack }: ControlsParams) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (<div className="Controls">
|
return (<div className="Controls">
|
||||||
<Accordion>
|
<Typography component="span" sx={{ mb: 1 }}>
|
||||||
<AccordionSummary
|
You can change the information available to the LLM by adjusting the following settings:
|
||||||
expandIcon={<ExpandMoreIcon />}
|
|
||||||
>
|
</Typography>
|
||||||
<Typography component="span">Tools</Typography>
|
|
||||||
</AccordionSummary>
|
|
||||||
<AccordionDetails>
|
|
||||||
These tools can be made available to the LLM for obtaining real-time information from the Internet.
|
|
||||||
</AccordionDetails>
|
|
||||||
<AccordionActions>
|
|
||||||
<FormGroup>
|
|
||||||
{
|
|
||||||
tools.map((tool, index) => {
|
|
||||||
return (<FormControlLabel key={index} control={<Switch checked={tool.enabled} />} onChange={toggleTool} label={tool.label} />);
|
|
||||||
})
|
|
||||||
}</FormGroup>
|
|
||||||
</AccordionActions>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion>
|
|
||||||
<AccordionSummary
|
|
||||||
expandIcon={<ExpandMoreIcon />}
|
|
||||||
>
|
|
||||||
<Typography component="span">RAG</Typography>
|
|
||||||
</AccordionSummary>
|
|
||||||
<AccordionDetails>
|
|
||||||
These RAG databases can be enabled / disabled for adding additional context based on the chat request.
|
|
||||||
</AccordionDetails>
|
|
||||||
<AccordionActions>
|
|
||||||
<FormGroup>
|
|
||||||
{
|
|
||||||
rags.map((rag, index) => {
|
|
||||||
return (<FormControlLabel key={index} control={<Switch checked={rag.enabled} />} onChange={toggleTool} label={rag.label} />);
|
|
||||||
})
|
|
||||||
}</FormGroup>
|
|
||||||
</AccordionActions>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion>
|
<Accordion>
|
||||||
<AccordionSummary
|
<AccordionSummary
|
||||||
expandIcon={<ExpandMoreIcon />}
|
expandIcon={<ExpandMoreIcon />}
|
||||||
@ -222,11 +314,57 @@ const Controls = ({ sessionId, connectionBase, setSnack }: ControlsParams) => {
|
|||||||
id="SystemPromptInput"
|
id="SystemPromptInput"
|
||||||
/>
|
/>
|
||||||
<div style={{ display: "flex", flexDirection: "row", gap: "8px", paddingTop: "8px" }}>
|
<div style={{ display: "flex", flexDirection: "row", gap: "8px", paddingTop: "8px" }}>
|
||||||
<Button variant="contained" onClick={sendSystemPrompt}>Set</Button>
|
<Button variant="contained" disabled={editSystemPrompt === systemPrompt} onClick={sendSystemPrompt}>Set</Button>
|
||||||
<Button variant="outlined" onClick={resetSystemPrompt} color="error">Reset</Button>
|
<Button variant="outlined" onClick={onResetSystemPrompt} color="error">Reset</Button>
|
||||||
</div>
|
</div>
|
||||||
</AccordionActions>
|
</AccordionActions>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
<Accordion>
|
||||||
|
<AccordionSummary
|
||||||
|
expandIcon={<ExpandMoreIcon />}
|
||||||
|
>
|
||||||
|
<Typography component="span">Tools</Typography>
|
||||||
|
</AccordionSummary>
|
||||||
|
<AccordionDetails>
|
||||||
|
These tools can be made available to the LLM for obtaining real-time information from the Internet. The description provided to the LLM is provided for reference.
|
||||||
|
</AccordionDetails>
|
||||||
|
<AccordionActions>
|
||||||
|
<FormGroup sx={{ p: 1 }}>
|
||||||
|
{
|
||||||
|
tools.map((tool, index) =>
|
||||||
|
<Box key={index}>
|
||||||
|
<Divider />
|
||||||
|
<FormControlLabel control={<Switch checked={tool.enabled} />} onChange={() => toggleTool("tool", index)} label={tool?.function?.name} />
|
||||||
|
<Typography>{tool?.function?.description}</Typography>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}</FormGroup>
|
||||||
|
</AccordionActions>
|
||||||
|
</Accordion>
|
||||||
|
<Accordion>
|
||||||
|
<AccordionSummary
|
||||||
|
expandIcon={<ExpandMoreIcon />}
|
||||||
|
>
|
||||||
|
<Typography component="span">RAG</Typography>
|
||||||
|
</AccordionSummary>
|
||||||
|
<AccordionDetails>
|
||||||
|
These RAG databases can be enabled / disabled for adding additional context based on the chat request.
|
||||||
|
</AccordionDetails>
|
||||||
|
<AccordionActions>
|
||||||
|
<FormGroup sx={{ p: 1 }}>
|
||||||
|
{
|
||||||
|
rags.map((rag, index) =>
|
||||||
|
<Box key={index}>
|
||||||
|
<Divider />
|
||||||
|
<FormControlLabel control={<Switch checked={rag.enabled} />} onChange={() => toggleTool("rag", index)} label={rag?.name} />
|
||||||
|
<Typography>{rag?.description}</Typography>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}</FormGroup>
|
||||||
|
</AccordionActions>
|
||||||
|
</Accordion>
|
||||||
|
<Button onClick={onClearContext}>Clear Chat History</Button>
|
||||||
|
<Button onClick={onResetToDefaults}>Reset to defaults</Button>
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,14 +381,6 @@ const App = () => {
|
|||||||
const [snackMessage, setSnackMessage] = useState("");
|
const [snackMessage, setSnackMessage] = useState("");
|
||||||
const [snackSeverity, setSnackSeverity] = useState<SeverityType>("success");
|
const [snackSeverity, setSnackSeverity] = useState<SeverityType>("success");
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (snackMessage === "") {
|
|
||||||
setSnackOpen(false);
|
|
||||||
} else {
|
|
||||||
setSnackOpen(true);
|
|
||||||
}
|
|
||||||
}, [snackMessage, setSnackOpen]);
|
|
||||||
|
|
||||||
// Scroll to bottom of conversation when conversation updates
|
// Scroll to bottom of conversation when conversation updates
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (conversationRef.current) {
|
if (conversationRef.current) {
|
||||||
@ -295,6 +425,7 @@ const App = () => {
|
|||||||
const setSnack = (message: string, severity: SeverityType = "success") => {
|
const setSnack = (message: string, severity: SeverityType = "success") => {
|
||||||
setSnackMessage(message);
|
setSnackMessage(message);
|
||||||
setSnackSeverity(severity);
|
setSnackSeverity(severity);
|
||||||
|
setSnackOpen(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDrawerClose = () => {
|
const handleDrawerClose = () => {
|
||||||
@ -312,9 +443,13 @@ const App = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onClearHistory = () => {
|
||||||
|
setConversation([welcomeMessage]);
|
||||||
|
};
|
||||||
|
|
||||||
const drawer = (
|
const drawer = (
|
||||||
<>
|
<>
|
||||||
{sessionId !== undefined && <Controls setSnack={setSnack} sessionId={sessionId} connectionBase={getConnectionBase(loc)} />}
|
{sessionId !== undefined && <Controls onClearHistory={onClearHistory} setSnack={setSnack} sessionId={sessionId} connectionBase={getConnectionBase(loc)} />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -399,36 +534,22 @@ const App = () => {
|
|||||||
const decoder = new TextDecoder();
|
const decoder = new TextDecoder();
|
||||||
let buffer = '';
|
let buffer = '';
|
||||||
|
|
||||||
// Debug message to console
|
|
||||||
console.log('Starting to process stream');
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const { done, value } = await reader.read();
|
const { done, value } = await reader.read();
|
||||||
|
|
||||||
if (done) {
|
if (done) {
|
||||||
console.log('Stream complete');
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert chunk to text and debug
|
|
||||||
const chunk = decoder.decode(value, { stream: true });
|
const chunk = decoder.decode(value, { stream: true });
|
||||||
console.log('Received chunk:', chunk);
|
|
||||||
|
|
||||||
// Add to buffer and process lines
|
|
||||||
buffer += chunk;
|
|
||||||
|
|
||||||
// Process complete lines
|
|
||||||
let lines = buffer.split('\n');
|
|
||||||
buffer = lines.pop() || ''; // Keep incomplete line in buffer
|
|
||||||
|
|
||||||
console.log(`Processing ${lines.length} complete lines`);
|
|
||||||
|
|
||||||
// Process each complete line immediately
|
// Process each complete line immediately
|
||||||
|
buffer += chunk;
|
||||||
|
let lines = buffer.split('\n');
|
||||||
|
buffer = lines.pop() || ''; // Keep incomplete line in buffer
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
if (!line.trim()) continue;
|
if (!line.trim()) continue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('Processing line:', line);
|
|
||||||
const update = JSON.parse(line);
|
const update = JSON.parse(line);
|
||||||
|
|
||||||
// Force an immediate state update based on the message type
|
// Force an immediate state update based on the message type
|
||||||
@ -457,6 +578,7 @@ const App = () => {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
setSnack("Error processing query", "error")
|
||||||
console.error('Error parsing JSON:', e, line);
|
console.error('Error parsing JSON:', e, line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -465,7 +587,6 @@ const App = () => {
|
|||||||
// Process any remaining buffer content
|
// Process any remaining buffer content
|
||||||
if (buffer.trim()) {
|
if (buffer.trim()) {
|
||||||
try {
|
try {
|
||||||
console.log('Processing final buffer:', buffer);
|
|
||||||
const update = JSON.parse(buffer);
|
const update = JSON.parse(buffer);
|
||||||
|
|
||||||
if (update.status === 'done') {
|
if (update.status === 'done') {
|
||||||
@ -475,7 +596,7 @@ const App = () => {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error parsing final buffer:', e);
|
setSnack("Error processing query", "error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -495,8 +616,6 @@ const App = () => {
|
|||||||
event: React.SyntheticEvent | Event,
|
event: React.SyntheticEvent | Event,
|
||||||
reason?: SnackbarCloseReason,
|
reason?: SnackbarCloseReason,
|
||||||
) => {
|
) => {
|
||||||
setSnackMessage("");
|
|
||||||
|
|
||||||
if (reason === 'clickaway') {
|
if (reason === 'clickaway') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,11 @@ from tools import (
|
|||||||
tools
|
tools
|
||||||
)
|
)
|
||||||
|
|
||||||
|
rags = [
|
||||||
|
{ "name": "JPK", "enabled": False, "description": "Expert data about James Ketrenos, including work history, personal hobbies, and projects." },
|
||||||
|
{ "name": "LKML", "enabled": False, "description": "Full associative data for entire LKML mailing list archive." },
|
||||||
|
]
|
||||||
|
|
||||||
# %%
|
# %%
|
||||||
# Defaults
|
# Defaults
|
||||||
OLLAMA_API_URL = "http://ollama:11434" # Default Ollama local endpoint
|
OLLAMA_API_URL = "http://ollama:11434" # Default Ollama local endpoint
|
||||||
@ -328,6 +333,13 @@ def is_valid_uuid(value):
|
|||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def default_tools(tools):
|
||||||
|
return [{**tool, "enabled": True} for tool in tools]
|
||||||
|
|
||||||
|
def llm_tools(tools):
|
||||||
|
return [tool for tool in tools if tool.get("enabled", False) == True]
|
||||||
|
|
||||||
# %%
|
# %%
|
||||||
class WebServer:
|
class WebServer:
|
||||||
def __init__(self, logging, client, model=MODEL_NAME):
|
def __init__(self, logging, client, model=MODEL_NAME):
|
||||||
@ -356,6 +368,35 @@ class WebServer:
|
|||||||
return RedirectResponse(url=f"/{context['id']}", status_code=307)
|
return RedirectResponse(url=f"/{context['id']}", status_code=307)
|
||||||
#return JSONResponse({"redirect": f"/{context['id']}"})
|
#return JSONResponse({"redirect": f"/{context['id']}"})
|
||||||
|
|
||||||
|
@self.app.put('/api/reset/{context_id}')
|
||||||
|
async def put_reset(context_id: str, request: Request):
|
||||||
|
if not is_valid_uuid(context_id):
|
||||||
|
logging.warning(f"Invalid context_id: {context_id}")
|
||||||
|
return JSONResponse({"error": "Invalid context_id"}, status_code=400)
|
||||||
|
context = self.upsert_context(context_id)
|
||||||
|
data = await request.json()
|
||||||
|
try:
|
||||||
|
for reset in data["reset"]:
|
||||||
|
match reset:
|
||||||
|
case "system-prompt":
|
||||||
|
context["system"] = [{"role": "system", "content": system_message}]
|
||||||
|
return JSONResponse(context["system"])
|
||||||
|
case "rag":
|
||||||
|
context["rag"] = rags.copy()
|
||||||
|
return JSONResponse(context["rag"])
|
||||||
|
case "tools":
|
||||||
|
context["tools"] = default_tools(tools)
|
||||||
|
return JSONResponse(context["tools"])
|
||||||
|
case "history":
|
||||||
|
context["history"] = []
|
||||||
|
return JSONResponse(context["history"])
|
||||||
|
|
||||||
|
return JSONResponse({ "error": "Usage: { reset: rag|tools|history|system-prompt}"}), 405
|
||||||
|
|
||||||
|
except:
|
||||||
|
return JSONResponse({ "error": "Usage: { reset: rag|tools|history|system-prompt}"}), 405
|
||||||
|
|
||||||
|
|
||||||
@self.app.put('/api/system-prompt/{context_id}')
|
@self.app.put('/api/system-prompt/{context_id}')
|
||||||
async def put_system_prompt(context_id: str, request: Request):
|
async def put_system_prompt(context_id: str, request: Request):
|
||||||
if not is_valid_uuid(context_id):
|
if not is_valid_uuid(context_id):
|
||||||
@ -365,7 +406,7 @@ class WebServer:
|
|||||||
data = await request.json()
|
data = await request.json()
|
||||||
system_prompt = data["system-prompt"].strip()
|
system_prompt = data["system-prompt"].strip()
|
||||||
if not system_prompt:
|
if not system_prompt:
|
||||||
system_prompt = system_message
|
return JSONResponse({ "status": "error", "message": "System prompt can not be empty." }), 405
|
||||||
context["system"] = [{"role": "system", "content": system_prompt}]
|
context["system"] = [{"role": "system", "content": system_prompt}]
|
||||||
return JSONResponse({ "system-prompt": system_prompt })
|
return JSONResponse({ "system-prompt": system_prompt })
|
||||||
|
|
||||||
@ -398,6 +439,7 @@ class WebServer:
|
|||||||
"X-Accel-Buffering": "no" # Prevents Nginx buffering if you're using it
|
"X-Accel-Buffering": "no" # Prevents Nginx buffering if you're using it
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@self.app.post('/api/context')
|
@self.app.post('/api/context')
|
||||||
async def create_context():
|
async def create_context():
|
||||||
context = self.create_context()
|
context = self.create_context()
|
||||||
@ -414,6 +456,29 @@ class WebServer:
|
|||||||
context = self.upsert_context(context_id)
|
context = self.upsert_context(context_id)
|
||||||
return JSONResponse(context["tools"])
|
return JSONResponse(context["tools"])
|
||||||
|
|
||||||
|
@self.app.put('/api/tools/{context_id}')
|
||||||
|
async def put_tools(context_id: str, request: Request):
|
||||||
|
if not is_valid_uuid(context_id):
|
||||||
|
logging.warning(f"Invalid context_id: {context_id}")
|
||||||
|
return JSONResponse({"error": "Invalid context_id"}, status_code=400)
|
||||||
|
context = self.upsert_context(context_id)
|
||||||
|
try:
|
||||||
|
data = await request.json()
|
||||||
|
modify = data["tool"]
|
||||||
|
enabled = data["enabled"]
|
||||||
|
for tool in context["tools"]:
|
||||||
|
if modify == tool["function"]["name"]:
|
||||||
|
tool["enabled"] = enabled
|
||||||
|
return JSONResponse(context["tools"])
|
||||||
|
return JSONResponse({ "status": f"{modify} not found in tools." }), 404
|
||||||
|
except:
|
||||||
|
return JSONResponse({ "status": "error" }), 405
|
||||||
|
|
||||||
|
@self.app.get('/api/rags/{context_id}')
|
||||||
|
async def get_rags(context_id: str):
|
||||||
|
context = self.upsert_context(context_id)
|
||||||
|
return JSONResponse(context["rags"])
|
||||||
|
|
||||||
@self.app.get('/api/health')
|
@self.app.get('/api/health')
|
||||||
async def health_check():
|
async def health_check():
|
||||||
return JSONResponse({"status": "healthy"})
|
return JSONResponse({"status": "healthy"})
|
||||||
@ -434,7 +499,8 @@ class WebServer:
|
|||||||
"id": context_id,
|
"id": context_id,
|
||||||
"system": [{"role": "system", "content": system_message}],
|
"system": [{"role": "system", "content": system_message}],
|
||||||
"history": [],
|
"history": [],
|
||||||
"tools": []
|
"tools": default_tools(tools),
|
||||||
|
"rags": rags.copy()
|
||||||
}
|
}
|
||||||
logging.info(f"{context_id} created and added to sessions.")
|
logging.info(f"{context_id} created and added to sessions.")
|
||||||
self.contexts[context_id] = context
|
self.contexts[context_id] = context
|
||||||
@ -472,7 +538,7 @@ class WebServer:
|
|||||||
yield {"status": "processing", "message": "Processing request..."}
|
yield {"status": "processing", "message": "Processing request..."}
|
||||||
|
|
||||||
# Use the async generator in an async for loop
|
# Use the async generator in an async for loop
|
||||||
response = self.client.chat(model=self.model, messages=messages, tools=tools)
|
response = self.client.chat(model=self.model, messages=messages, tools=llm_tools(context["tools"]))
|
||||||
tools_used = []
|
tools_used = []
|
||||||
|
|
||||||
yield {"status": "processing", "message": "Initial response received"}
|
yield {"status": "processing", "message": "Initial response received"}
|
||||||
|
@ -352,11 +352,13 @@ tools = [ {
|
|||||||
"properties": {
|
"properties": {
|
||||||
"city": {
|
"city": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "City to find the weather forecast (e.g., 'Portland', 'Seattle')."
|
"description": "City to find the weather forecast (e.g., 'Portland', 'Seattle').",
|
||||||
|
"minLength": 2
|
||||||
},
|
},
|
||||||
"state": {
|
"state": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "State to find the weather forecast (e.g., 'OR', 'WA')."
|
"description": "State to find the weather forecast (e.g., 'OR', 'WA').",
|
||||||
|
"minLength": 2
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [ "city", "state" ],
|
"required": [ "city", "state" ],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user