update front_end

This commit is contained in:
2026-03-04 16:04:42 +08:00
parent 9c0744e1bc
commit f6547ca6fe
3 changed files with 38 additions and 130 deletions

View File

@@ -17,7 +17,7 @@ import { chooseActiveConfigItem, chooseDisplayItemsByPipeline } from "./activeCo
import type { import type {
GraphConfigListItem, GraphConfigListItem,
GraphConfigReadResponse, GraphConfigReadResponse,
PipelineRunInfo, PipelineSpec,
} from "./types"; } from "./types";
type EditableAgent = { type EditableAgent = {
@@ -28,24 +28,13 @@ type EditableAgent = {
promptSetId?: string; promptSetId?: string;
toolKeys: string[]; toolKeys: string[];
prompts: Record<string, string>; prompts: Record<string, string>;
port: number;
apiKey: string; apiKey: string;
llmName: string; llmName: string;
}; };
type LaunchCredentials = {
url: string;
authType: string;
authHeaderName: string;
authKey: string;
authKeyMasked: string;
};
type ActiveTab = "agents" | "mcp"; type ActiveTab = "agents" | "mcp";
const DEFAULT_ENTRY_POINT = "fastapi_server/server_dashscope.py";
const DEFAULT_LLM_NAME = "qwen-plus"; const DEFAULT_LLM_NAME = "qwen-plus";
const DEFAULT_PORT = 8100;
const DEFAULT_API_KEY = ""; const DEFAULT_API_KEY = "";
const GRAPH_ARCH_IMAGE_MODULES = import.meta.glob( const GRAPH_ARCH_IMAGE_MODULES = import.meta.glob(
"../assets/images/graph_arch/*.{png,jpg,jpeg,webp,gif}", "../assets/images/graph_arch/*.{png,jpg,jpeg,webp,gif}",
@@ -117,7 +106,6 @@ function toEditable(
promptSetId: config.prompt_set_id, promptSetId: config.prompt_set_id,
toolKeys: config.tool_keys || [], toolKeys: config.tool_keys || [],
prompts: config.prompt_dict || {}, prompts: config.prompt_dict || {},
port: DEFAULT_PORT,
apiKey: config.api_key || DEFAULT_API_KEY, apiKey: config.api_key || DEFAULT_API_KEY,
llmName: DEFAULT_LLM_NAME, llmName: DEFAULT_LLM_NAME,
}; };
@@ -127,12 +115,11 @@ export default function App() {
const [activeTab, setActiveTab] = useState<ActiveTab>("agents"); const [activeTab, setActiveTab] = useState<ActiveTab>("agents");
const [graphs, setGraphs] = useState<string[]>([]); const [graphs, setGraphs] = useState<string[]>([]);
const [configItems, setConfigItems] = useState<GraphConfigListItem[]>([]); const [configItems, setConfigItems] = useState<GraphConfigListItem[]>([]);
const [running, setRunning] = useState<PipelineRunInfo[]>([]); const [running, setRunning] = useState<PipelineSpec[]>([]);
const [draftAgents, setDraftAgents] = useState<EditableAgent[]>([]); const [draftAgents, setDraftAgents] = useState<EditableAgent[]>([]);
const [selectedId, setSelectedId] = useState<string | null>(null); const [selectedId, setSelectedId] = useState<string | null>(null);
const [editor, setEditor] = useState<EditableAgent | null>(null); const [editor, setEditor] = useState<EditableAgent | null>(null);
const [statusMessage, setStatusMessage] = useState<string>(""); const [statusMessage, setStatusMessage] = useState<string>("");
const [launchCredentials, setLaunchCredentials] = useState<LaunchCredentials | null>(null);
const [mcpConfigPath, setMcpConfigPath] = useState<string>(""); const [mcpConfigPath, setMcpConfigPath] = useState<string>("");
const [mcpConfigRaw, setMcpConfigRaw] = useState<string>(""); const [mcpConfigRaw, setMcpConfigRaw] = useState<string>("");
const [mcpToolKeys, setMcpToolKeys] = useState<string[]>([]); const [mcpToolKeys, setMcpToolKeys] = useState<string[]>([]);
@@ -248,7 +235,6 @@ export default function App() {
} }
const editable = toEditable(detail, false); const editable = toEditable(detail, false);
editable.id = id; editable.id = id;
editable.port = editor?.pipelineId === editable.pipelineId ? editor.port : DEFAULT_PORT;
editable.llmName = editor?.pipelineId === editable.pipelineId ? editor.llmName : DEFAULT_LLM_NAME; editable.llmName = editor?.pipelineId === editable.pipelineId ? editor.llmName : DEFAULT_LLM_NAME;
// apiKey is loaded from backend (persisted in DB) — don't override with default // apiKey is loaded from backend (persisted in DB) — don't override with default
setEditor(editable); setEditor(editable);
@@ -432,7 +418,6 @@ export default function App() {
const detail = await getPipelineDefaultConfig(upsertResp.pipeline_id); const detail = await getPipelineDefaultConfig(upsertResp.pipeline_id);
const saved = toEditable(detail, false); const saved = toEditable(detail, false);
saved.id = makeAgentKey(upsertResp.pipeline_id); saved.id = makeAgentKey(upsertResp.pipeline_id);
saved.port = editor.port;
// apiKey is loaded from backend (persisted in DB) — don't override // apiKey is loaded from backend (persisted in DB) — don't override
saved.llmName = editor.llmName; saved.llmName = editor.llmName;
setEditor(saved); setEditor(saved);
@@ -485,10 +470,6 @@ export default function App() {
setStatusMessage("pipeline_id is required before run."); setStatusMessage("pipeline_id is required before run.");
return; return;
} }
if (!Number.isInteger(editor.port) || editor.port <= 0) {
setStatusMessage("port must be a positive integer.");
return;
}
if (!editor.apiKey.trim()) { if (!editor.apiKey.trim()) {
setStatusMessage("api_key is required before run."); setStatusMessage("api_key is required before run.");
return; return;
@@ -496,27 +477,20 @@ export default function App() {
setBusy(true); setBusy(true);
setStatusMessage("Starting agent..."); setStatusMessage("Starting agent...");
setLaunchCredentials(null);
try { try {
const resp = await createPipeline({ const resp = await createPipeline({
graph_id: editor.graphId, graph_id: editor.graphId,
pipeline_id: editor.pipelineId.trim(), pipeline_id: editor.pipelineId.trim(),
prompt_set_id: editor.promptSetId, prompt_set_id: editor.promptSetId,
tool_keys: editor.toolKeys, tool_keys: editor.toolKeys,
port: editor.port,
api_key: editor.apiKey.trim(), api_key: editor.apiKey.trim(),
entry_point: DEFAULT_ENTRY_POINT,
llm_name: editor.llmName, llm_name: editor.llmName,
enabled: true,
}); });
await refreshRunning(); await refreshRunning();
setStatusMessage(`Agent started. URL: ${resp.url}`); setStatusMessage(
setLaunchCredentials({ `Agent registered. config_file=${resp.config_file}, reload_required=${String(resp.reload_required)}`
url: resp.url, );
authType: resp.auth_type,
authHeaderName: resp.auth_header_name,
authKey: resp.auth_key_once,
authKeyMasked: resp.auth_key_masked,
});
} catch (error) { } catch (error) {
setStatusMessage((error as Error).message); setStatusMessage((error as Error).message);
} finally { } finally {
@@ -537,7 +511,7 @@ export default function App() {
setBusy(true); setBusy(true);
setStatusMessage("Stopping agent..."); setStatusMessage("Stopping agent...");
try { try {
await stopPipeline(target.run_id); await stopPipeline(target.pipeline_id);
await refreshRunning(); await refreshRunning();
setStatusMessage("Agent stopped."); setStatusMessage("Agent stopped.");
} catch (error) { } catch (error) {
@@ -562,23 +536,6 @@ export default function App() {
})), })),
]; ];
const graphArchImage = editor ? getGraphArchImage(editor.graphId) : null; const graphArchImage = editor ? getGraphArchImage(editor.graphId) : null;
const authHeaderValue = launchCredentials
? `${launchCredentials.authHeaderName}: Bearer ${launchCredentials.authKey}`
: "";
const canUseClipboard = typeof navigator !== "undefined" && Boolean(navigator.clipboard);
async function copyText(text: string, label: string): Promise<void> {
if (!canUseClipboard) {
setStatusMessage(`Clipboard is not available. Please copy ${label} manually.`);
return;
}
try {
await navigator.clipboard.writeText(text);
setStatusMessage(`${label} copied.`);
} catch {
setStatusMessage(`Failed to copy ${label}.`);
}
}
const showSidebar = activeTab === "agents"; const showSidebar = activeTab === "agents";
@@ -660,48 +617,6 @@ export default function App() {
</button> </button>
</div> </div>
{launchCredentials ? (
<div className="launch-credentials">
<h3>Access Credentials (shown once)</h3>
<div>
<strong>URL:</strong>{" "}
<a href={launchCredentials.url} target="_blank" rel="noreferrer">
{launchCredentials.url}
</a>
<button
type="button"
onClick={() => copyText(launchCredentials.url, "URL")}
disabled={busy}
>
Copy URL
</button>
</div>
<div>
<strong>{launchCredentials.authType} key:</strong> {launchCredentials.authKey}
<button
type="button"
onClick={() => copyText(launchCredentials.authKey, "auth key")}
disabled={busy}
>
Copy Key
</button>
</div>
<div>
<strong>Header:</strong> <code>{authHeaderValue}</code>
<button
type="button"
onClick={() => copyText(authHeaderValue, "auth header")}
disabled={busy}
>
Copy Header
</button>
</div>
<p className="empty">
Stored after launch as masked value: {launchCredentials.authKeyMasked}
</p>
</div>
) : null}
{!editor ? ( {!editor ? (
<div className="empty-panel"> <div className="empty-panel">
<p>Select an agent from the left or create a new one.</p> <p>Select an agent from the left or create a new one.</p>
@@ -756,17 +671,6 @@ export default function App() {
/> />
</label> </label>
<label>
port
<input
type="number"
min={1}
value={editor.port}
onChange={(e) => updateEditor("port", Number(e.target.value))}
disabled={busy}
/>
</label>
<label> <label>
api_key api_key
<input <input
@@ -815,21 +719,21 @@ export default function App() {
<p className="empty">No active runs for this agent.</p> <p className="empty">No active runs for this agent.</p>
) : ( ) : (
selectedRuns.map((run) => ( selectedRuns.map((run) => (
<div key={run.run_id} className="run-card"> <div key={run.pipeline_id} className="run-card">
<div> <div>
<strong>run_id:</strong> {run.run_id} <strong>pipeline_id:</strong> {run.pipeline_id}
</div> </div>
<div> <div>
<strong>pid:</strong> {run.pid} <strong>graph_id:</strong> {run.graph_id}
</div> </div>
<div> <div>
<strong>url:</strong>{" "} <strong>model:</strong> {run.llm_name}
<a href={run.url} target="_blank" rel="noreferrer">
{run.url}
</a>
</div> </div>
<div> <div>
<strong>auth:</strong> {run.auth_header_name} Bearer {run.auth_key_masked} <strong>enabled:</strong> {String(run.enabled)}
</div>
<div>
<strong>config_file:</strong> {run.config_file}
</div> </div>
</div> </div>
)) ))

View File

@@ -120,8 +120,8 @@ export function listPipelines(): Promise<PipelineListResponse> {
return fetchJson("/v1/pipelines"); return fetchJson("/v1/pipelines");
} }
export function stopPipeline(runId: string): Promise<PipelineStopResponse> { export function stopPipeline(pipelineId: string): Promise<PipelineStopResponse> {
return fetchJson(`/v1/pipelines/${runId}`, { return fetchJson(`/v1/pipelines/${pipelineId}`, {
method: "DELETE", method: "DELETE",
}); });
} }

View File

@@ -52,37 +52,41 @@ export type PipelineCreateRequest = {
pipeline_id: string; pipeline_id: string;
prompt_set_id: string; prompt_set_id: string;
tool_keys: string[]; tool_keys: string[];
port: number; api_key?: string;
api_key: string;
entry_point: string;
llm_name: string; llm_name: string;
enabled?: boolean;
}; };
export type PipelineRunInfo = { export type PipelineSpec = {
run_id: string; pipeline_id: string;
pid: number;
graph_id: string; graph_id: string;
enabled: boolean;
config_file: string;
llm_name: string;
overrides: Record<string, unknown>;
};
export type PipelineCreateResponse = {
pipeline_id: string; pipeline_id: string;
prompt_set_id: string; prompt_set_id: string;
url: string; graph_id: string;
port: number; config_file: string;
auth_type: string; llm_name: string;
auth_header_name: string; enabled: boolean;
auth_key_masked: string; reload_required: boolean;
}; registry_path: string;
export type PipelineCreateResponse = PipelineRunInfo & {
auth_key_once: string;
}; };
export type PipelineListResponse = { export type PipelineListResponse = {
items: PipelineRunInfo[]; items: PipelineSpec[];
count: number; count: number;
}; };
export type PipelineStopResponse = { export type PipelineStopResponse = {
run_id: string; pipeline_id: string;
status: string; status: string;
enabled: boolean;
reload_required: boolean;
}; };
export type McpToolConfigResponse = { export type McpToolConfigResponse = {