feat(chat): 完善运行状态与权限交互
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
import { fetchAgentRuntimeHealth } from "./agentRuntime";
|
||||
|
||||
const apiFetch = jest.fn();
|
||||
|
||||
jest.mock("@/lib/apiFetch", () => ({
|
||||
apiFetch: (...args: unknown[]) => apiFetch(...args),
|
||||
}));
|
||||
|
||||
describe("fetchAgentRuntimeHealth", () => {
|
||||
beforeEach(() => {
|
||||
apiFetch.mockReset();
|
||||
});
|
||||
|
||||
it("reports ready only after runtime warmup is complete", async () => {
|
||||
apiFetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
ok: true,
|
||||
ready: true,
|
||||
warmed_up: true,
|
||||
runtime: { healthy: true },
|
||||
}),
|
||||
});
|
||||
|
||||
await expect(fetchAgentRuntimeHealth()).resolves.toBe(true);
|
||||
expect(apiFetch).toHaveBeenCalledWith(
|
||||
expect.stringContaining("/health"),
|
||||
expect.objectContaining({
|
||||
method: "GET",
|
||||
projectHeaderMode: "omit",
|
||||
skipAuthRedirect: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("reports unavailable when runtime health is not ready", async () => {
|
||||
apiFetch.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
json: async () => ({ ok: false, ready: false, warmed_up: true }),
|
||||
});
|
||||
|
||||
await expect(fetchAgentRuntimeHealth()).resolves.toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
import { apiFetch } from "@/lib/apiFetch";
|
||||
import { config } from "@config/config";
|
||||
|
||||
export type AgentRuntimeState =
|
||||
| "checking"
|
||||
| "ready"
|
||||
| "unavailable"
|
||||
| "models_unavailable";
|
||||
|
||||
type AgentHealthPayload = {
|
||||
ok?: unknown;
|
||||
ready?: unknown;
|
||||
warmed_up?: unknown;
|
||||
runtime?: {
|
||||
healthy?: unknown;
|
||||
};
|
||||
};
|
||||
|
||||
export const fetchAgentRuntimeHealth = async (
|
||||
signal?: AbortSignal,
|
||||
): Promise<boolean> => {
|
||||
const response = await apiFetch(`${config.AGENT_URL}/health`, {
|
||||
method: "GET",
|
||||
signal,
|
||||
projectHeaderMode: "omit",
|
||||
skipAuthRedirect: true,
|
||||
});
|
||||
const payload = (await response.json().catch(() => null)) as AgentHealthPayload | null;
|
||||
|
||||
return Boolean(
|
||||
response.ok &&
|
||||
payload?.ok === true &&
|
||||
payload.ready === true &&
|
||||
payload.warmed_up === true &&
|
||||
payload.runtime?.healthy === true,
|
||||
);
|
||||
};
|
||||
@@ -46,9 +46,10 @@ const normalizeModelOption = (value: unknown): AgentModelOption | null => {
|
||||
};
|
||||
};
|
||||
|
||||
export const fetchAgentModels = async (): Promise<AgentModelConfig> => {
|
||||
export const fetchAgentModels = async (signal?: AbortSignal): Promise<AgentModelConfig> => {
|
||||
const response = await apiFetch(`${config.AGENT_URL}/api/v1/agent/models`, {
|
||||
method: "GET",
|
||||
signal,
|
||||
projectHeaderMode: "include",
|
||||
skipAuthRedirect: true,
|
||||
});
|
||||
|
||||
@@ -390,6 +390,7 @@ describe("streamAgentChat", () => {
|
||||
skipAuthRedirect: true,
|
||||
}),
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
it("calls permission reply endpoint", async () => {
|
||||
@@ -413,6 +414,19 @@ describe("streamAgentChat", () => {
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
await replyAgentPermission("s1", "perm-2", "always");
|
||||
|
||||
expect(apiFetch).toHaveBeenLastCalledWith(
|
||||
expect.stringContaining("/api/v1/agent/sessions/s1/permission-responses"),
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
request_id: "perm-2",
|
||||
reply: "always",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("submits refreshed credentials through the authenticated context", async () => {
|
||||
|
||||
@@ -8,8 +8,9 @@ const getAgentSessionUrl = (sessionId: string, suffix = "") =>
|
||||
|
||||
export type AgentModel = string;
|
||||
|
||||
export type PermissionReply = "once" | "always" | "reject";
|
||||
export type AgentApprovalMode = "request" | "always";
|
||||
export type PermissionDecision = "once" | "always" | "reject";
|
||||
export type PermissionReply = PermissionDecision;
|
||||
export type AgentApprovalMode = "request" | "auto" | "always";
|
||||
|
||||
export type AgentQuestionStatus =
|
||||
| "pending"
|
||||
@@ -664,7 +665,7 @@ export const abortAgentChat = async (sessionId?: string) => {
|
||||
export const replyAgentPermission = async (
|
||||
sessionId: string,
|
||||
requestId: string,
|
||||
reply: PermissionReply,
|
||||
reply: PermissionDecision,
|
||||
) => {
|
||||
const response = await apiFetch(
|
||||
getAgentSessionUrl(sessionId, "/permission-responses"),
|
||||
|
||||
Reference in New Issue
Block a user