feat: add skill evaluation workbench
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import { getCurrentAccount } from "@/lib/claw-auth";
|
||||
|
||||
const CLAW_API_URL = process.env.CLAW_API_URL ?? "http://127.0.0.1:8765";
|
||||
|
||||
type RouteContext = {
|
||||
params: Promise<{ path: string[] }>;
|
||||
};
|
||||
|
||||
export async function GET(request: Request, context: RouteContext) {
|
||||
return proxyEvaluationRequest("GET", request, context);
|
||||
}
|
||||
|
||||
export async function POST(request: Request, context: RouteContext) {
|
||||
return proxyEvaluationRequest("POST", request, context);
|
||||
}
|
||||
|
||||
export async function PATCH(request: Request, context: RouteContext) {
|
||||
return proxyEvaluationRequest("PATCH", request, context);
|
||||
}
|
||||
|
||||
async function proxyEvaluationRequest(
|
||||
method: "GET" | "POST" | "PATCH",
|
||||
request: Request,
|
||||
context: RouteContext,
|
||||
) {
|
||||
const account = await getCurrentAccount();
|
||||
if (!account)
|
||||
return Response.json({ detail: "unauthorized" }, { status: 401 });
|
||||
|
||||
const { path } = await context.params;
|
||||
const incomingUrl = new URL(request.url);
|
||||
const targetUrl = new URL(
|
||||
`${CLAW_API_URL}/api/evaluations/${path.map(encodeURIComponent).join("/")}`,
|
||||
);
|
||||
for (const [key, value] of incomingUrl.searchParams.entries()) {
|
||||
targetUrl.searchParams.append(key, value);
|
||||
}
|
||||
|
||||
const headers: Record<string, string> = {};
|
||||
let body: string | undefined;
|
||||
if (method === "GET") {
|
||||
targetUrl.searchParams.set("account_id", account.id);
|
||||
} else {
|
||||
const payload = (await request.json()) as Record<string, unknown>;
|
||||
headers["content-type"] = "application/json";
|
||||
body = JSON.stringify({ ...payload, account_id: account.id });
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(targetUrl, {
|
||||
method,
|
||||
headers,
|
||||
body,
|
||||
cache: "no-store",
|
||||
});
|
||||
const responseHeaders = new Headers();
|
||||
responseHeaders.set(
|
||||
"content-type",
|
||||
response.headers.get("content-type") ?? "application/json",
|
||||
);
|
||||
const disposition = response.headers.get("content-disposition");
|
||||
if (disposition) responseHeaders.set("content-disposition", disposition);
|
||||
return new Response(await response.arrayBuffer(), {
|
||||
status: response.status,
|
||||
headers: responseHeaders,
|
||||
});
|
||||
} catch (error) {
|
||||
return Response.json(
|
||||
{
|
||||
detail:
|
||||
error instanceof Error
|
||||
? `Unable to reach Claw backend: ${error.message}`
|
||||
: "Unable to reach Claw backend",
|
||||
},
|
||||
{ status: 502 },
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ import {
|
||||
import { ThreadListPrimitive } from "@assistant-ui/react";
|
||||
import type { UIMessage } from "ai";
|
||||
import {
|
||||
FlaskConicalIcon,
|
||||
Loader2Icon,
|
||||
MoreHorizontalIcon,
|
||||
PencilIcon,
|
||||
@@ -148,6 +149,7 @@ export const ThreadList: FC = () => {
|
||||
return (
|
||||
<ThreadListPrimitive.Root className="aui-root aui-thread-list-root flex min-h-0 flex-1 flex-col gap-1">
|
||||
<ThreadListNew />
|
||||
<ThreadListEvaluation />
|
||||
<JupyterWorkspaceList />
|
||||
<ClawSessionList />
|
||||
</ThreadListPrimitive.Root>
|
||||
@@ -174,6 +176,21 @@ const ThreadListNew: FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const ThreadListEvaluation: FC = () => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="h-9 w-full justify-start gap-2 rounded-lg px-3 text-sm"
|
||||
asChild
|
||||
>
|
||||
<a href="/evaluations">
|
||||
<FlaskConicalIcon className="size-4" />
|
||||
Skill 评测
|
||||
</a>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
type JupyterWorkspaceEntry = {
|
||||
id: string;
|
||||
label?: string;
|
||||
@@ -1220,7 +1237,7 @@ function resolvePendingPrompt(
|
||||
: "";
|
||||
if (!pendingPrompt) return null;
|
||||
for (const message of messages) {
|
||||
if (!message || message.role !== "user") continue;
|
||||
if (message?.role !== "user") continue;
|
||||
const content = cleanStoredContent(message.content ?? "").trim();
|
||||
if (!content || content.trimStart().startsWith("<system-reminder>"))
|
||||
continue;
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
BrainIcon,
|
||||
ChevronDownIcon,
|
||||
Clock3Icon,
|
||||
FlaskConicalIcon,
|
||||
LogOutIcon,
|
||||
PanelLeftOpenIcon,
|
||||
SaveIcon,
|
||||
@@ -215,6 +216,12 @@ function CollapsedSidebarRail({
|
||||
>
|
||||
<SquarePenIcon className="size-4" />
|
||||
</CollapsedIconButton>
|
||||
<CollapsedIconButton
|
||||
label="Skill 评测"
|
||||
onClick={() => window.location.assign("/evaluations")}
|
||||
>
|
||||
<FlaskConicalIcon className="size-4" />
|
||||
</CollapsedIconButton>
|
||||
<CollapsedSessionSearch />
|
||||
<CollapsedRecentSessions />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user