Wip web interface
This commit is contained in:
@@ -180,4 +180,5 @@ class AutoApprover:
|
|||||||
return
|
return
|
||||||
if (self._response_enabled and
|
if (self._response_enabled and
|
||||||
self.agent.llms[self._llm_name] == LlmState.IDLE):
|
self.agent.llms[self._llm_name] == LlmState.IDLE):
|
||||||
self.agent.approve_response(self._llm_name, self.agent.get_output(self._llm_name))
|
response = self.agent.response_buffer.get_text()
|
||||||
|
self.agent.approve_response()
|
||||||
|
|||||||
@@ -94,6 +94,8 @@ class Api:
|
|||||||
"""Approve current buffer content"""
|
"""Approve current buffer content"""
|
||||||
data = await request.json()
|
data = await request.json()
|
||||||
try:
|
try:
|
||||||
|
response = data.get("response")
|
||||||
|
self._agent.response_buffer.set_text(response)
|
||||||
self._agent.approve_response()
|
self._agent.approve_response()
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
@@ -147,10 +149,10 @@ class Api:
|
|||||||
"""Get all LLMs and their current states."""
|
"""Get all LLMs and their current states."""
|
||||||
states = self._agent.llms
|
states = self._agent.llms
|
||||||
return web.Response(
|
return web.Response(
|
||||||
text=json.dumps({
|
text=json.dumps(
|
||||||
name: state.name
|
[{"name": name, "state": state.name}
|
||||||
for name, state in states.items()
|
for name, state in states.items()]
|
||||||
}),
|
),
|
||||||
content_type="application/json"
|
content_type="application/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -234,10 +236,12 @@ class Api:
|
|||||||
"""Get complete working memory state."""
|
"""Get complete working memory state."""
|
||||||
entries = self._working_memory.get_entries()
|
entries = self._working_memory.get_entries()
|
||||||
return web.Response(
|
return web.Response(
|
||||||
text=json.dumps([e.serialize() for e in entries]),
|
text=json.dumps({
|
||||||
|
"type": "memory_state",
|
||||||
|
"entries": [e.serialize() for e in entries]
|
||||||
|
}),
|
||||||
content_type="application/json"
|
content_type="application/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _create_entry(self, request: web.Request) -> web.Response:
|
async def _create_entry(self, request: web.Request) -> web.Response:
|
||||||
"""Create a new entry in working memory."""
|
"""Create a new entry in working memory."""
|
||||||
data = await request.json()
|
data = await request.json()
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class ResponseWebSocket:
|
|||||||
|
|
||||||
async def _handle_buffer_change(self, buffer):
|
async def _handle_buffer_change(self, buffer):
|
||||||
await self._broadcast_message({
|
await self._broadcast_message({
|
||||||
"buffer": buffer,
|
"response": buffer,
|
||||||
})
|
})
|
||||||
|
|
||||||
async def _broadcast_message(self, message):
|
async def _broadcast_message(self, message):
|
||||||
@@ -32,7 +32,7 @@ class ResponseWebSocket:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
await ws.send_json({
|
await ws.send_json({
|
||||||
"buffer": self._agent.response_buffer.get_text(),
|
"response": self._agent.response_buffer.get_text(),
|
||||||
})
|
})
|
||||||
|
|
||||||
async for msg in ws:
|
async for msg in ws:
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class Websockets:
|
|||||||
|
|
||||||
app.router.add_get("/ws/auto_approver", self._auto_approver_ws.handle_connection)
|
app.router.add_get("/ws/auto_approver", self._auto_approver_ws.handle_connection)
|
||||||
app.router.add_get("/ws/context", self._context_ws.handle_connection)
|
app.router.add_get("/ws/context", self._context_ws.handle_connection)
|
||||||
app.router.add_get("/ws/llm", self._llm_ws.handle_connection)
|
app.router.add_get("/ws/llms", self._llm_ws.handle_connection)
|
||||||
app.router.add_get("/ws/memory", self._memory_ws.handle_connection)
|
app.router.add_get("/ws/memory", self._memory_ws.handle_connection)
|
||||||
app.router.add_get("/ws/response", self._response_ws.handle_connection)
|
app.router.add_get("/ws/response", self._response_ws.handle_connection)
|
||||||
app.router.add_get("/ws/stdout", self._stdout_ws.handle_connection)
|
app.router.add_get("/ws/stdout", self._stdout_ws.handle_connection)
|
||||||
|
|||||||
@@ -132,6 +132,8 @@ class WebAgent(BaseAgent):
|
|||||||
|
|
||||||
def approve_response(self) -> None:
|
def approve_response(self) -> None:
|
||||||
"""Process approved response from specified LLM"""
|
"""Process approved response from specified LLM"""
|
||||||
|
if self.llms.get(llm_name) != LlmState.IDLE:
|
||||||
|
return
|
||||||
timestamp = datetime.now(timezone.utc)
|
timestamp = datetime.now(timezone.utc)
|
||||||
self._iteration_logger.log_iteration(timestamp, self._context, self._response_buffer.get_text())
|
self._iteration_logger.log_iteration(timestamp, self._context, self._response_buffer.get_text())
|
||||||
parse_result = self._parser.parse(timestamp, self._response_buffer.get_text())
|
parse_result = self._parser.parse(timestamp, self._response_buffer.get_text())
|
||||||
|
|||||||
@@ -8,9 +8,8 @@ import { StandardEditor } from '@/components/editors/StandardEditor';
|
|||||||
import { useWebSocket, WebSocketState } from '@/hooks/useWebSocket';
|
import { useWebSocket, WebSocketState } from '@/hooks/useWebSocket';
|
||||||
|
|
||||||
export const LlmState = {
|
export const LlmState = {
|
||||||
NO_OUTPUT: 'NO_OUTPUT',
|
IDLE: 'IDLE',
|
||||||
INFERENCE: 'INFERENCE',
|
INFERENCE: 'INFERENCE'
|
||||||
OUTPUT: 'OUTPUT'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
@@ -18,8 +17,8 @@ const App = () => {
|
|||||||
const [generatedContext, setGeneratedContext] = useState('');
|
const [generatedContext, setGeneratedContext] = useState('');
|
||||||
const [modifiedContext, setModifiedContext] = useState('');
|
const [modifiedContext, setModifiedContext] = useState('');
|
||||||
const [contextDirty, setContextDirty] = useState(false);
|
const [contextDirty, setContextDirty] = useState(false);
|
||||||
const [generatedResponses, setGeneratedResponses] = useState({});
|
const [generatedResponse, setGeneratedResponse] = useState('');
|
||||||
const [modifiedResponses, setModifiedResponses] = useState({});
|
const [modifiedResponse, setModifiedResponse] = useState('');
|
||||||
const [input, setInput] = useState('');
|
const [input, setInput] = useState('');
|
||||||
const [output, setOutput] = useState('');
|
const [output, setOutput] = useState('');
|
||||||
|
|
||||||
@@ -44,9 +43,9 @@ const App = () => {
|
|||||||
|
|
||||||
// WebSocket connections
|
// WebSocket connections
|
||||||
const wsRoot = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws`
|
const wsRoot = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws`
|
||||||
const llmWs = useWebSocket(`${wsRoot}/llm`);
|
const llmWs = useWebSocket(`${wsRoot}/llms`);
|
||||||
const contextWs = useWebSocket(`${wsRoot}/context`);
|
const contextWs = useWebSocket(`${wsRoot}/context`);
|
||||||
const tokenWs = useWebSocket(`${wsRoot}/token`);
|
const responseWs = useWebSocket(`${wsRoot}/response`);
|
||||||
const stdoutWs = useWebSocket(`${wsRoot}/stdout`);
|
const stdoutWs = useWebSocket(`${wsRoot}/stdout`);
|
||||||
const autoApproverWs = useWebSocket(`${wsRoot}/auto_approver`);
|
const autoApproverWs = useWebSocket(`${wsRoot}/auto_approver`);
|
||||||
const memoryWs = useWebSocket(`${wsRoot}/memory`);
|
const memoryWs = useWebSocket(`${wsRoot}/memory`);
|
||||||
@@ -60,18 +59,6 @@ const App = () => {
|
|||||||
...prevLlms,
|
...prevLlms,
|
||||||
[data.llm]: data.state
|
[data.llm]: data.state
|
||||||
}));
|
}));
|
||||||
if (data.state === LlmState.NO_OUTPUT) {
|
|
||||||
setGeneratedResponses(prev => {
|
|
||||||
const updated = { ...prev };
|
|
||||||
delete updated[data.llm];
|
|
||||||
return updated;
|
|
||||||
});
|
|
||||||
setModifiedResponses(prev => {
|
|
||||||
const updated = { ...prev };
|
|
||||||
delete updated[data.llm];
|
|
||||||
return updated;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -86,17 +73,11 @@ const App = () => {
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Handle incoming tokens
|
// Handle response changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
tokenWs.addMessageHandler((data) => {
|
responseWs.addMessageHandler((data) => {
|
||||||
setGeneratedResponses(prev => ({
|
setModifiedResponse(data.response);
|
||||||
...prev,
|
setGeneratedResponse(data.response);
|
||||||
[data.llm]: (prev[data.llm] || '') + data.token
|
|
||||||
}));
|
|
||||||
setModifiedResponses(prev => ({
|
|
||||||
...prev,
|
|
||||||
[data.llm]: (prev[data.llm] || '') + data.token
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -138,28 +119,33 @@ const App = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleApprove = () => {
|
const handleApprove = () => {
|
||||||
const state = llms[activeLlm];
|
fetch('/api/response/approve', {
|
||||||
if (state === LlmState.NO_OUTPUT) {
|
method: 'POST',
|
||||||
if (contextDirty) {
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ response: modifiedResponse })
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleInference = () => {
|
||||||
fetch('/api/context', {
|
fetch('/api/context', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ context: modifiedContext })
|
body: JSON.stringify({ context: modifiedContext })
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
fetch('/api/response', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ response: modifiedResponse })
|
||||||
|
});
|
||||||
|
|
||||||
fetch(`/api/inference/${activeLlm}`, {
|
fetch(`/api/inference/${activeLlm}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
});
|
});
|
||||||
} else if (state === LlmState.OUTPUT) {
|
|
||||||
fetch(`/api/approve/${activeLlm}`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ response: modifiedResponses[activeLlm] })
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleStop = () => {
|
const handleStop = () => {
|
||||||
|
// We still specify which LLM to stop inference for
|
||||||
fetch(`/api/inference/${activeLlm}/stop`, {
|
fetch(`/api/inference/${activeLlm}/stop`, {
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
});
|
});
|
||||||
@@ -182,14 +168,22 @@ const App = () => {
|
|||||||
|
|
||||||
const handleContextEdit = (context) => {
|
const handleContextEdit = (context) => {
|
||||||
setContextDirty(true);
|
setContextDirty(true);
|
||||||
setGeneratedResponses({});
|
// When context is edited, we need to reset all LLM states to IDLE
|
||||||
setModifiedResponses({});
|
// since responses are no longer valid
|
||||||
const resetLlms = {};
|
const resetLlms = {};
|
||||||
for (const llm of Object.keys(llms)) {
|
for (const llm of Object.keys(llms)) {
|
||||||
resetLlms[llm] = LlmState.NO_OUTPUT;
|
resetLlms[llm] = LlmState.IDLE;
|
||||||
}
|
}
|
||||||
setLlms(resetLlms);
|
setLlms(resetLlms);
|
||||||
setModifiedContext(context);
|
setModifiedContext(context);
|
||||||
|
|
||||||
|
// Reset response buffers
|
||||||
|
setGeneratedResponse('');
|
||||||
|
setModifiedResponse('');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleResponseEdit = (response) => {
|
||||||
|
setModifiedResponse(response);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAutoApproverConfigChange = async (config) => {
|
const handleAutoApproverConfigChange = async (config) => {
|
||||||
@@ -246,37 +240,21 @@ const App = () => {
|
|||||||
if (!response.ok) throw new Error('Failed to update entry');
|
if (!response.ok) throw new Error('Failed to update entry');
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update active tab on state change
|
// Determine active LLM's current state for UI feedback
|
||||||
useEffect(() => {
|
const activeLlmState = activeLlm ? llms[activeLlm] : null;
|
||||||
if (activeTab === Tabs.MEMORY) return;
|
|
||||||
const state = llms[activeLlm];
|
|
||||||
switch (state) {
|
|
||||||
case null:
|
|
||||||
break;
|
|
||||||
case LlmState.NO_OUTPUT:
|
|
||||||
setActiveTab(Tabs.CONTEXT);
|
|
||||||
setShowDiff(false);
|
|
||||||
setShowSidebar(false);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
setActiveTab(Tabs.RESPONSE);
|
|
||||||
setShowDiff(false);
|
|
||||||
setShowSidebar(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}, [llms[activeLlm]]);
|
|
||||||
|
|
||||||
|
// Track overall WebSocket connection state
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (llmWs.wsState === WebSocketState.CONNECTED &&
|
if (llmWs.wsState === WebSocketState.CONNECTED &&
|
||||||
contextWs.wsState === WebSocketState.CONNECTED &&
|
contextWs.wsState === WebSocketState.CONNECTED &&
|
||||||
tokenWs.wsState === WebSocketState.CONNECTED &&
|
responseWs.wsState === WebSocketState.CONNECTED &&
|
||||||
stdoutWs.wsState === WebSocketState.CONNECTED &&
|
stdoutWs.wsState === WebSocketState.CONNECTED &&
|
||||||
autoApproverWs.wsState === WebSocketState.CONNECTED &&
|
autoApproverWs.wsState === WebSocketState.CONNECTED &&
|
||||||
memoryWs.wsState === WebSocketState.CONNECTED) {
|
memoryWs.wsState === WebSocketState.CONNECTED) {
|
||||||
setWsState(WebSocketState.CONNECTED);
|
setWsState(WebSocketState.CONNECTED);
|
||||||
} else if (llmWs.wsState === WebSocketState.CONNECTING ||
|
} else if (llmWs.wsState === WebSocketState.CONNECTING ||
|
||||||
contextWs.wsState === WebSocketState.CONNECTING ||
|
contextWs.wsState === WebSocketState.CONNECTING ||
|
||||||
tokenWs.wsState === WebSocketState.CONNECTING ||
|
responseWs.wsState === WebSocketState.CONNECTING ||
|
||||||
stdoutWs.wsState === WebSocketState.CONNECTING ||
|
stdoutWs.wsState === WebSocketState.CONNECTING ||
|
||||||
autoApproverWs.wsState === WebSocketState.CONNECTING ||
|
autoApproverWs.wsState === WebSocketState.CONNECTING ||
|
||||||
memoryWs.wsState === WebSocketState.CONNECTING) {
|
memoryWs.wsState === WebSocketState.CONNECTING) {
|
||||||
@@ -284,13 +262,13 @@ const App = () => {
|
|||||||
} else {
|
} else {
|
||||||
setWsState(WebSocketState.DISCONNECTED);
|
setWsState(WebSocketState.DISCONNECTED);
|
||||||
}
|
}
|
||||||
}, [llmWs.wsState, contextWs.wsState, tokenWs.wsState, stdoutWs.wsState, autoApproverWs.wsState, memoryWs.wsState]);
|
}, [llmWs.wsState, contextWs.wsState, responseWs.wsState, stdoutWs.wsState, autoApproverWs.wsState, memoryWs.wsState]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-screen bg-gray-100 flex flex-col overflow-hidden">
|
<div className="h-screen bg-gray-100 flex flex-col overflow-hidden">
|
||||||
<Header
|
<Header
|
||||||
wsState={wsState}
|
wsState={wsState}
|
||||||
agentState={llms[activeLlm]}
|
agentState={activeLlmState}
|
||||||
activeTab={activeTab}
|
activeTab={activeTab}
|
||||||
setActiveTab={setActiveTab}
|
setActiveTab={setActiveTab}
|
||||||
/>
|
/>
|
||||||
@@ -309,12 +287,9 @@ const App = () => {
|
|||||||
{activeTab === Tabs.RESPONSE && (
|
{activeTab === Tabs.RESPONSE && (
|
||||||
<ContentEditor
|
<ContentEditor
|
||||||
showDiff={showDiff}
|
showDiff={showDiff}
|
||||||
originalContent={generatedResponses[activeLlm] || ''}
|
originalContent={generatedResponse}
|
||||||
modifiedContent={modifiedResponses[activeLlm] || ''}
|
modifiedContent={modifiedResponse}
|
||||||
onChange={value => setModifiedResponses(prev => ({
|
onChange={handleResponseEdit}
|
||||||
...prev,
|
|
||||||
[activeLlm]: value
|
|
||||||
}))}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{activeTab === Tabs.INPUT && (
|
{activeTab === Tabs.INPUT && (
|
||||||
@@ -347,14 +322,15 @@ const App = () => {
|
|||||||
showSidebar={showSidebar}
|
showSidebar={showSidebar}
|
||||||
llms={llms}
|
llms={llms}
|
||||||
activeLlm={activeLlm}
|
activeLlm={activeLlm}
|
||||||
llmState={llms[activeLlm]}
|
llmState={activeLlmState}
|
||||||
showDiff={showDiff}
|
showDiff={showDiff}
|
||||||
autoScroll={autoScroll}
|
autoScroll={autoScroll}
|
||||||
onAutoScrollChange={(enabled) => setAutoScroll(enabled)}
|
onAutoScrollChange={setAutoScroll}
|
||||||
input={input}
|
input={input}
|
||||||
output={output}
|
output={output}
|
||||||
autoApproverConfig={autoApproverConfig}
|
autoApproverConfig={autoApproverConfig}
|
||||||
onApprove={handleApprove}
|
onApprove={handleApprove}
|
||||||
|
onInference={handleInference}
|
||||||
onStop={handleStop}
|
onStop={handleStop}
|
||||||
onToggleDiff={() => setShowDiff(!showDiff)}
|
onToggleDiff={() => setShowDiff(!showDiff)}
|
||||||
onSendInput={handleSendInput}
|
onSendInput={handleSendInput}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ export const Tabs = {
|
|||||||
INPUT: 'INPUT',
|
INPUT: 'INPUT',
|
||||||
OUTPUT: 'OUTPUT',
|
OUTPUT: 'OUTPUT',
|
||||||
MEMORY: 'MEMORY'
|
MEMORY: 'MEMORY'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Header = ({
|
export const Header = ({
|
||||||
@@ -31,9 +30,11 @@ export const Header = ({
|
|||||||
}`}>
|
}`}>
|
||||||
{wsState}
|
{wsState}
|
||||||
</div>
|
</div>
|
||||||
|
{agentState && (
|
||||||
<div className="px-3 py-1 rounded-full bg-blue-100 text-blue-800 text-sm">
|
<div className="px-3 py-1 rounded-full bg-blue-100 text-blue-800 text-sm">
|
||||||
{agentState}
|
{agentState}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="border-t">
|
<div className="border-t">
|
||||||
@@ -48,7 +49,7 @@ export const Header = ({
|
|||||||
: 'border-transparent text-gray-500 hover:text-gray-700'
|
: 'border-transparent text-gray-500 hover:text-gray-700'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{tab.charAt(0).toUpperCase() + tab.slice(1)}
|
{tab.charAt(0).toUpperCase() + tab.slice(1).toLowerCase()}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export const Sidebar = ({
|
|||||||
output,
|
output,
|
||||||
autoApproverConfig,
|
autoApproverConfig,
|
||||||
onApprove,
|
onApprove,
|
||||||
|
onInference,
|
||||||
onStop,
|
onStop,
|
||||||
onToggleDiff,
|
onToggleDiff,
|
||||||
onSendInput,
|
onSendInput,
|
||||||
@@ -61,15 +62,27 @@ export const Sidebar = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{llmState === LlmState.INFERENCE ? (
|
||||||
<Button
|
<Button
|
||||||
onClick={llmState === LlmState.INFERENCE ? onStop : onApprove}
|
onClick={onStop}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
{
|
Stop
|
||||||
llmState === LlmState.NO_OUTPUT ? 'Inference' :
|
</Button>
|
||||||
llmState === LlmState.INFERENCE ? 'Stop' :
|
) : (
|
||||||
'Approve'
|
<Button
|
||||||
}
|
onClick={onInference}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
Start Inference
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Button
|
||||||
|
onClick={onApprove}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
Approve Response
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button onClick={onSendInput} disabled={!input.trim()} className="w-full">
|
<Button onClick={onSendInput} disabled={!input.trim()} className="w-full">
|
||||||
@@ -136,7 +149,7 @@ export const Sidebar = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<label className="text-sm">Auto-Approve LLM</label>
|
<label className="text-sm">LLM for Inference</label>
|
||||||
<Select
|
<Select
|
||||||
value={autoApproverConfig.llm_name}
|
value={autoApproverConfig.llm_name}
|
||||||
onValueChange={(llm) => onAutoApproverConfigChange({
|
onValueChange={(llm) => onAutoApproverConfigChange({
|
||||||
@@ -153,6 +166,9 @@ export const Sidebar = ({
|
|||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">
|
||||||
|
This selects which model will generate content during auto-approval.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user