Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,13 @@ describe('StudioMemberInvokePanel', () => {
expect(screen.getByText('Status')).toBeTruthy();
expect(screen.getByText('Run ID')).toBeTruthy();
expect(screen.getByText('Command ID')).toBeTruthy();
expect(screen.getAllByText('Actor ID').length).toBeGreaterThanOrEqual(1);
expect(screen.getByText('Member ID')).toBeTruthy();
expect(screen.getByText('Elapsed')).toBeTruthy();
expect(screen.getByText('尚未开始')).toBeTruthy();
expect(screen.getByText('尚未发出')).toBeTruthy();
expect(screen.getAllByText('actor-default').length).toBeGreaterThanOrEqual(1);
expect(screen.getByText('default')).toBeTruthy();
expect(screen.getByText('Member')).toBeTruthy();
expect(screen.getByText('Published Context')).toBeTruthy();
expect(screen.getByText('Revision')).toBeTruthy();
Expand All @@ -138,6 +142,7 @@ describe('StudioMemberInvokePanel', () => {
expect(screen.getByText('Events')).toBeTruthy();
expect(screen.getByText('Advanced typed payload')).toBeTruthy();
expect(screen.getByTestId('studio-invoke-playground-actions')).toBeTruthy();
expect(screen.getByRole('button', { name: '打开运行记录' })).toBeDisabled();
expect(
screen.getByText('No conversation yet. Send a prompt to start the run.'),
).toBeTruthy();
Expand Down Expand Up @@ -364,6 +369,12 @@ describe('StudioMemberInvokePanel', () => {
expect(inlineScope.getByText('Actor ID')).toBeTruthy();
expect(inlineScope.getByText('actor-1')).toBeTruthy();
expect(inlineScope.getByText('Duration')).toBeTruthy();
expect(screen.getByRole('button', { name: '打开运行记录' })).toBeEnabled();

fireEvent.click(screen.getByRole('button', { name: '打开运行记录' }));

expect(window.location.pathname).toBe('/runtime/runs');
expect(new URLSearchParams(window.location.search).get('runId')).toBe('run-1');

fireEvent.change(screen.getByLabelText('调用请求输入'), {
target: {
Expand All @@ -374,6 +385,55 @@ describe('StudioMemberInvokePanel', () => {
expect(screen.getByLabelText('调用请求输入')).toHaveValue('Overwrite prompt');
});

it('does not enable Runs navigation when invoke only returns a command id', async () => {
(runtimeRunsApi.invokeEndpoint as jest.Mock).mockResolvedValueOnce({
accepted: true,
commandId: 'cmd-only',
targetActorId: 'actor-1',
});

render(
React.createElement(StudioMemberInvokePanel, {
memberId: 'default',
scopeId: 'scope-1',
services: [
{
deploymentStatus: 'Active',
displayName: 'workspace-demo',
endpoints: [
{
description: 'Send a structured request into the member.',
displayName: 'Submit',
endpointId: 'submit',
kind: 'invoke',
requestTypeUrl: 'type.googleapis.com/example.Submit',
responseTypeUrl: 'type.googleapis.com/example.SubmitResult',
},
],
kind: 'service',
namespace: 'default',
primaryActorId: 'actor-default',
serviceId: 'default',
},
],
}),
);

fireEvent.change(await screen.findByLabelText('调用请求输入'), {
target: {
value: 'Dispatch this typed command.',
},
});
fireEvent.click(screen.getByRole('button', { name: 'Invoke' }));

await waitFor(() => {
expect(runtimeRunsApi.invokeEndpoint).toHaveBeenCalled();
expect(screen.getAllByText('cmd-only').length).toBeGreaterThanOrEqual(1);
});

expect(screen.getByRole('button', { name: '打开运行记录' })).toBeDisabled();
});

it('renders a clear empty state when no selected member is available for invoke', async () => {
render(
React.createElement(StudioMemberInvokePanel, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,9 @@ const StudioMemberInvokePanel: React.FC<StudioMemberInvokePanelProps> = ({
);
const runIdLabel = trimOptional(invokeResult.runId) || '尚未开始';
const commandIdLabel = trimOptional(invokeResult.commandId) || '尚未发出';
const actorIdLabel =
trimOptional(invokeResult.actorId) || currentMemberActorId || '尚未分配';
const memberIdLabel = normalizedMemberId || '未选中成员';
const endpointLabel = selectedEndpoint?.displayName || selectedEndpointId || '—';

useEffect(() => {
Expand Down Expand Up @@ -1028,7 +1031,8 @@ const StudioMemberInvokePanel: React.FC<StudioMemberInvokePanelProps> = ({
]);

const handleOpenRuns = useCallback(() => {
if (!scopeId || !normalizedMemberId || !selectedEndpoint) {
const currentRunId = trimOptional(invokeResult.runId);
if (!scopeId || !normalizedMemberId || !selectedEndpoint || !currentRunId) {
return;
}

Expand Down Expand Up @@ -1066,6 +1070,7 @@ const StudioMemberInvokePanel: React.FC<StudioMemberInvokePanelProps> = ({
payloadTypeUrl: currentPayloadTypeUrl || undefined,
prompt: currentPrompt || undefined,
returnTo: returnTo || undefined,
runId: currentRunId,
scopeId,
serviceId: selectedService?.serviceId,
}),
Expand Down Expand Up @@ -1155,6 +1160,18 @@ const StudioMemberInvokePanel: React.FC<StudioMemberInvokePanelProps> = ({
{commandIdLabel}
</div>
</div>
<div style={runSummaryCardStyle}>
<div style={runSummaryLabelStyle}>Actor ID</div>
<div title={actorIdLabel} style={runSummaryValueStyle}>
{actorIdLabel}
</div>
</div>
<div style={runSummaryCardStyle}>
<div style={runSummaryLabelStyle}>Member ID</div>
<div title={memberIdLabel} style={runSummaryValueStyle}>
{memberIdLabel}
</div>
</div>
<div style={runSummaryCardStyle}>
<div style={runSummaryLabelStyle}>Elapsed</div>
<div style={runSummaryValueStyle}>{runElapsedLabel}</div>
Expand Down Expand Up @@ -1185,7 +1202,7 @@ const StudioMemberInvokePanel: React.FC<StudioMemberInvokePanelProps> = ({
effectiveResponseTypeUrl={effectiveResponseTypeUrl}
endpointKind={selectedEndpoint?.kind || 'command'}
formError={formError}
hasOpenRunsTarget={Boolean(scopeId && selectedEndpoint)}
hasOpenRunsTarget={Boolean(trimOptional(invokeResult.runId))}
invokeStatus={invokeResult.status}
isChatEndpoint={isChatEndpoint}
layout="dock"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ describe('runtimeRoutes', () => {
expect(
buildRuntimeRunsHref({
actorId: 'actor://selected',
runId: 'run-1',
returnTo: buildRuntimeExplorerHref({
actorId: 'actor://selected',
runId: 'run-1',
}),
}),
).toContain(
'returnTo=%2Fruntime%2Fexplorer%2Fdetail%3FactorId%3Dactor%253A%252F%252Fselected%26runId%3Drun-1',
'actorId=actor%3A%2F%2Fselected&runId=run-1&returnTo=%2Fruntime%2Fexplorer%2Fdetail%3FactorId%3Dactor%253A%252F%252Fselected%26runId%3Drun-1',
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export function buildRuntimeRunsHref(options?: {
payloadTypeUrl?: string;
payloadBase64?: string;
actorId?: string;
runId?: string;
draftKey?: string;
returnTo?: string;
}): string {
Expand All @@ -71,6 +72,7 @@ export function buildRuntimeRunsHref(options?: {
payloadTypeUrl: options?.payloadTypeUrl,
payloadBase64: options?.payloadBase64,
actorId: options?.actorId,
runId: options?.runId,
draftKey: options?.draftKey,
returnTo: options?.returnTo,
});
Expand Down
11 changes: 11 additions & 0 deletions apps/aevatar-console-web/src/shared/runs/scopeConsole.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,17 @@ describe("scopeConsole", () => {
correlationId: "corr-1",
runId: "run-1",
});
expect(
extractRuntimeInvokeReceipt({
commandId: "cmd-only",
targetActorId: "actor://svc",
})
).toEqual({
actorId: "actor://svc",
commandId: "cmd-only",
correlationId: "",
runId: "",
});

expect(
getPreferredScopeConsoleServiceId(
Expand Down
9 changes: 4 additions & 5 deletions apps/aevatar-console-web/src/shared/runs/scopeConsole.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,8 @@ export function extractRuntimeInvokeReceipt(
response,
"request_id",
"requestId",
"command_id",
"commandId"
"run_id",
"runId"
);

return {
Expand All @@ -180,9 +180,8 @@ export function extractRuntimeInvokeReceipt(
"targetActorId",
"actorId"
),
commandId: readResponseField(response, "command_id", "commandId") || runId,
correlationId:
readResponseField(response, "correlation_id", "correlationId") || runId,
commandId: readResponseField(response, "command_id", "commandId"),
correlationId: readResponseField(response, "correlation_id", "correlationId"),
runId,
};
}
Loading