mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-09-17 07:17:18 +00:00
feat(runner): unify plugin execution across agents and event processors
This commit is contained in:
@@ -23,12 +23,12 @@ import {
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import PipelineDetailContent from '@/app/home/pipelines/PipelineDetailContent';
|
||||
import EventProcessorDetailContent from './EventProcessorDetailContent';
|
||||
import PluginProcessorDetailContent from './PluginProcessorDetailContent';
|
||||
import AgentCreateContent from './components/AgentCreateContent';
|
||||
import AgentDebugPanel from './components/AgentDebugPanel';
|
||||
import AgentFormComponent, {
|
||||
AgentFormHandle,
|
||||
AgentRunnerStatus,
|
||||
RunnerStatus,
|
||||
} from './components/AgentFormComponent';
|
||||
|
||||
export default function AgentDetailContent({ id }: { id: string }) {
|
||||
@@ -49,9 +49,7 @@ export default function AgentDetailContent({ id }: { id: string }) {
|
||||
const [basicInfoOpen, setBasicInfoOpen] = useState(false);
|
||||
const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
|
||||
const [deleting, setDeleting] = useState(false);
|
||||
const [runnerStatus, setRunnerStatus] = useState<AgentRunnerStatus | null>(
|
||||
null,
|
||||
);
|
||||
const [runnerStatus, setRunnerStatus] = useState<RunnerStatus | null>(null);
|
||||
const [availableEventTypes, setAvailableEventTypes] = useState<string[]>([
|
||||
'message.received',
|
||||
]);
|
||||
@@ -169,7 +167,7 @@ export default function AgentDetailContent({ id }: { id: string }) {
|
||||
return (
|
||||
<>
|
||||
{agent.kind === 'event_processor' ? (
|
||||
<EventProcessorDetailContent
|
||||
<PluginProcessorDetailContent
|
||||
key={id}
|
||||
id={id}
|
||||
agent={agent}
|
||||
|
||||
+8
-8
@@ -8,7 +8,7 @@ import { toast } from 'sonner';
|
||||
import type {
|
||||
Agent,
|
||||
AgentPlatformTool,
|
||||
EventProcessorDescriptor,
|
||||
RunnerDescriptor,
|
||||
ProcessorRun,
|
||||
ProcessorRunEvent,
|
||||
} from '@/app/infra/entities/api';
|
||||
@@ -21,14 +21,14 @@ import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import ProcessorDetailWorkbench from '@/app/home/components/processor-detail/ProcessorDetailWorkbench';
|
||||
import EntityTitleEditButton from '@/app/home/components/entity-basic-info/EntityTitleEditButton';
|
||||
import AgentDebugPanel from './components/AgentDebugPanel';
|
||||
import EventProcessorTrace, {
|
||||
import PluginProcessorTrace, {
|
||||
ProcessorPayload,
|
||||
} from './components/EventProcessorTrace';
|
||||
} from './components/PluginProcessorTrace';
|
||||
import ProcessorRunList from './components/ProcessorRunList';
|
||||
import EventProcessorSettings from './components/EventProcessorSettings';
|
||||
import PluginProcessorSettings from './components/PluginProcessorSettings';
|
||||
import DynamicFormComponent from '@/app/home/components/dynamic-form/DynamicFormComponent';
|
||||
|
||||
export default function EventProcessorDetailContent({
|
||||
export default function PluginProcessorDetailContent({
|
||||
agent,
|
||||
id,
|
||||
canManage,
|
||||
@@ -53,7 +53,7 @@ export default function EventProcessorDetailContent({
|
||||
const toolLabels = Object.fromEntries(
|
||||
platformTools.map((tool) => [tool.name, extractI18nObject(tool.label)]),
|
||||
);
|
||||
const [components, setComponents] = useState<EventProcessorDescriptor[]>([]);
|
||||
const [components, setComponents] = useState<RunnerDescriptor[]>([]);
|
||||
const [componentRef, setComponentRef] = useState(agent.component_ref ?? '');
|
||||
const initialParameters =
|
||||
(
|
||||
@@ -358,7 +358,7 @@ export default function EventProcessorDetailContent({
|
||||
value={selected.metadata.delivery}
|
||||
/>
|
||||
)}
|
||||
<EventProcessorTrace events={events} toolLabels={toolLabels} />
|
||||
<PluginProcessorTrace events={events} toolLabels={toolLabels} />
|
||||
{selected.status === 'failed' && selected.status_reason && (
|
||||
<Alert variant="destructive">
|
||||
<AlertDescription className="break-words">
|
||||
@@ -389,7 +389,7 @@ export default function EventProcessorDetailContent({
|
||||
canManage ? <EntityTitleEditButton onClick={onEdit} /> : undefined
|
||||
}
|
||||
titleControls={
|
||||
<EventProcessorSettings
|
||||
<PluginProcessorSettings
|
||||
components={components}
|
||||
value={componentRef}
|
||||
disabled={!canManage || saving || loading}
|
||||
@@ -46,7 +46,7 @@ import {
|
||||
groupEventPatterns,
|
||||
} from '@/app/home/components/event-patterns/event-pattern-groups';
|
||||
import EventSelectOptionContent from '@/app/home/components/event-patterns/EventSelectOptionContent';
|
||||
import EventProcessorTrace from './EventProcessorTrace';
|
||||
import PluginProcessorTrace from './PluginProcessorTrace';
|
||||
import AgentExecutionTrace from './AgentExecutionTrace';
|
||||
import AgentEventDataEditor from './AgentEventDataEditor';
|
||||
import {
|
||||
@@ -455,7 +455,7 @@ export default function AgentDebugPanel({
|
||||
</div>
|
||||
{entry.events &&
|
||||
(processor ? (
|
||||
<EventProcessorTrace
|
||||
<PluginProcessorTrace
|
||||
events={entry.events}
|
||||
toolLabels={toolLabels}
|
||||
/>
|
||||
|
||||
@@ -28,10 +28,10 @@ import {
|
||||
import DynamicFormComponent from '@/app/home/components/dynamic-form/DynamicFormComponent';
|
||||
import {
|
||||
getErrorMessage,
|
||||
readPendingAgentRunnerInstall,
|
||||
resumePendingAgentRunnerInstall,
|
||||
type InstalledAgentRunner,
|
||||
} from '@/app/home/agents/agent-runner-marketplace';
|
||||
readPendingRunnerInstall,
|
||||
resumePendingRunnerInstall,
|
||||
type InstalledRunner,
|
||||
} from '@/app/home/agents/runner-marketplace';
|
||||
import { extractI18nObject } from '@/i18n/I18nProvider';
|
||||
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import {
|
||||
@@ -43,7 +43,7 @@ import {
|
||||
} from '@/components/ui/card';
|
||||
import { Form, FormField, FormItem, FormMessage } from '@/components/ui/form';
|
||||
import AgentEventPatternPicker from './AgentEventPatternPicker';
|
||||
import AgentRunnerSelect from './AgentRunnerSelect';
|
||||
import RunnerSelect from './RunnerSelect';
|
||||
import AgentApiToolPicker from './AgentApiToolPicker';
|
||||
|
||||
const OTHER_TOOL_SCOPES = [
|
||||
@@ -54,7 +54,7 @@ const OTHER_TOOL_SCOPES = [
|
||||
'skill',
|
||||
] as const;
|
||||
|
||||
export interface AgentRunnerStatus {
|
||||
export interface RunnerStatus {
|
||||
label: string;
|
||||
description?: string;
|
||||
tone: 'neutral' | 'success' | 'warning' | 'error';
|
||||
@@ -66,7 +66,7 @@ interface AgentFormComponentProps {
|
||||
onFinish: (agent?: Partial<Agent>) => void;
|
||||
onDirtyChange?: (dirty: boolean) => void;
|
||||
onSavingChange?: (saving: boolean) => void;
|
||||
onRunnerStatusChange?: (status: AgentRunnerStatus) => void;
|
||||
onRunnerStatusChange?: (status: RunnerStatus) => void;
|
||||
onSupportedEventPatternsChange?: (patterns: string[]) => void;
|
||||
onPlatformToolsChange?: (tools: AgentPlatformTool[]) => void;
|
||||
}
|
||||
@@ -184,12 +184,9 @@ function AgentFormComponent(
|
||||
});
|
||||
const runnerInstallScope = `agent:${agentId}`;
|
||||
|
||||
const applyInstalledRunner = useCallback(
|
||||
(installed: InstalledAgentRunner) => {
|
||||
setRunnerConfigSchema(installed.configTab);
|
||||
},
|
||||
[],
|
||||
);
|
||||
const applyInstalledRunner = useCallback((installed: InstalledRunner) => {
|
||||
setRunnerConfigSchema(installed.configTab);
|
||||
}, []);
|
||||
|
||||
const savedSnapshotRef = useRef('');
|
||||
const initializedStagesRef = useRef<Set<string>>(new Set());
|
||||
@@ -285,15 +282,12 @@ function AgentFormComponent(
|
||||
}, [agentId, form, t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!initialDataLoaded ||
|
||||
!readPendingAgentRunnerInstall(runnerInstallScope)
|
||||
) {
|
||||
if (!initialDataLoaded || !readPendingRunnerInstall(runnerInstallScope)) {
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
setRunnerInstallRecovering(true);
|
||||
void resumePendingAgentRunnerInstall(runnerInstallScope)
|
||||
void resumePendingRunnerInstall(runnerInstallScope)
|
||||
.then((installed) => {
|
||||
if (cancelled || !installed) return;
|
||||
applyInstalledRunner(installed);
|
||||
@@ -395,7 +389,7 @@ function AgentFormComponent(
|
||||
},
|
||||
];
|
||||
|
||||
const runnerStatus = useMemo<AgentRunnerStatus>(() => {
|
||||
const runnerStatus = useMemo<RunnerStatus>(() => {
|
||||
if (pluginStatusLoading) {
|
||||
return {
|
||||
label: t('agents.runnerStatusLoading'),
|
||||
@@ -540,7 +534,7 @@ function AgentFormComponent(
|
||||
isRunnerSelector
|
||||
? ({ config, field }) =>
|
||||
config.name === 'id' ? (
|
||||
<AgentRunnerSelect
|
||||
<RunnerSelect
|
||||
options={config.options ?? []}
|
||||
label={extractI18nObject(config.label)}
|
||||
value={String(field.value ?? '')}
|
||||
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
import { Puzzle } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import type { EventProcessorDescriptor } from '@/app/infra/entities/api';
|
||||
import type { RunnerDescriptor } from '@/app/infra/entities/api';
|
||||
import { httpClient } from '@/app/infra/http';
|
||||
import { extractI18nObject } from '@/i18n/I18nProvider';
|
||||
import {
|
||||
@@ -18,7 +18,7 @@ function ProcessorComponentContent({
|
||||
component,
|
||||
option = false,
|
||||
}: {
|
||||
component: EventProcessorDescriptor;
|
||||
component: RunnerDescriptor;
|
||||
option?: boolean;
|
||||
}) {
|
||||
const label = extractI18nObject({
|
||||
@@ -62,13 +62,13 @@ function ProcessorComponentContent({
|
||||
);
|
||||
}
|
||||
|
||||
export default function EventProcessorSettings({
|
||||
export default function PluginProcessorSettings({
|
||||
components,
|
||||
value,
|
||||
onChange,
|
||||
disabled = false,
|
||||
}: {
|
||||
components: EventProcessorDescriptor[];
|
||||
components: RunnerDescriptor[];
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
disabled?: boolean;
|
||||
+1
-1
@@ -38,7 +38,7 @@ export function ProcessorPayload({
|
||||
);
|
||||
}
|
||||
|
||||
export default function EventProcessorTrace({
|
||||
export default function PluginProcessorTrace({
|
||||
events,
|
||||
toolLabels = {},
|
||||
}: {
|
||||
@@ -373,7 +373,7 @@ function PipelineDiagram() {
|
||||
);
|
||||
}
|
||||
|
||||
function EventProcessorDiagram() {
|
||||
function RunnerDiagram() {
|
||||
const { t } = useTranslation();
|
||||
const codeLines = [
|
||||
<>
|
||||
@@ -459,6 +459,6 @@ function EventProcessorDiagram() {
|
||||
}
|
||||
|
||||
export default function ProcessorTypeDiagram({ kind }: { kind: AgentKind }) {
|
||||
if (kind === 'event_processor') return <EventProcessorDiagram />;
|
||||
if (kind === 'event_processor') return <RunnerDiagram />;
|
||||
return kind === 'agent' ? <AgentDiagram /> : <PipelineDiagram />;
|
||||
}
|
||||
|
||||
+21
-21
@@ -7,17 +7,17 @@ import { getCloudServiceClientSync, httpClient } from '@/app/infra/http';
|
||||
import type { IDynamicFormItemOption } from '@/app/infra/entities/form/dynamic';
|
||||
import type { PluginV4 } from '@/app/infra/entities/plugin';
|
||||
import {
|
||||
AgentRunnerMarketplaceError,
|
||||
RunnerMarketplaceError,
|
||||
getErrorMessage,
|
||||
installMarketplaceAgentRunner,
|
||||
loadAgentRunnerCatalog,
|
||||
installMarketplaceRunner,
|
||||
loadRunnerCatalog,
|
||||
marketplacePluginId,
|
||||
runnerPluginPrefix,
|
||||
readPendingAgentRunnerInstall,
|
||||
subscribePendingAgentRunnerInstall,
|
||||
type AgentRunnerCatalog,
|
||||
type InstalledAgentRunner,
|
||||
} from '@/app/home/agents/agent-runner-marketplace';
|
||||
readPendingRunnerInstall,
|
||||
subscribePendingRunnerInstall,
|
||||
type RunnerCatalog,
|
||||
type InstalledRunner,
|
||||
} from '@/app/home/agents/runner-marketplace';
|
||||
import {
|
||||
InstallStage,
|
||||
usePluginInstallTasks,
|
||||
@@ -39,7 +39,7 @@ function installErrorMessage(
|
||||
error: unknown,
|
||||
t: ReturnType<typeof useTranslation>['t'],
|
||||
) {
|
||||
if (error instanceof AgentRunnerMarketplaceError) {
|
||||
if (error instanceof RunnerMarketplaceError) {
|
||||
if (error.code === 'version-unavailable') {
|
||||
return t('wizard.aiEngine.versionUnavailable');
|
||||
}
|
||||
@@ -123,7 +123,7 @@ function runnerPluginId(optionName: string) {
|
||||
function installedRunnerDescription(
|
||||
option: IDynamicFormItemOption,
|
||||
marketplaceRunners: PluginV4[],
|
||||
installedPluginDescriptions: AgentRunnerCatalog['installedPluginDescriptions'],
|
||||
installedPluginDescriptions: RunnerCatalog['installedPluginDescriptions'],
|
||||
) {
|
||||
const pluginId = runnerPluginId(option.name);
|
||||
if (!pluginId) return option.name;
|
||||
@@ -192,7 +192,7 @@ function MarketplaceRunnerContent({
|
||||
);
|
||||
}
|
||||
|
||||
export default function AgentRunnerSelect({
|
||||
export default function RunnerSelect({
|
||||
options,
|
||||
label,
|
||||
value,
|
||||
@@ -205,18 +205,18 @@ export default function AgentRunnerSelect({
|
||||
value: string;
|
||||
onValueChange: (value: string) => void;
|
||||
installScope: string;
|
||||
onInstalled: (installed: InstalledAgentRunner) => void;
|
||||
onInstalled: (installed: InstalledRunner) => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const { addTask, tasks } = usePluginInstallTasks();
|
||||
const [marketplaceRunners, setMarketplaceRunners] = useState<PluginV4[]>([]);
|
||||
const [installedPluginIds, setInstalledPluginIds] = useState<string[]>([]);
|
||||
const [installedPluginDescriptions, setInstalledPluginDescriptions] =
|
||||
useState<AgentRunnerCatalog['installedPluginDescriptions']>({});
|
||||
useState<RunnerCatalog['installedPluginDescriptions']>({});
|
||||
const [catalogLoading, setCatalogLoading] = useState(true);
|
||||
const [catalogError, setCatalogError] = useState(false);
|
||||
const [pendingInstall, setPendingInstall] = useState(() =>
|
||||
readPendingAgentRunnerInstall(installScope),
|
||||
readPendingRunnerInstall(installScope),
|
||||
);
|
||||
const [installError, setInstallError] = useState<string | null>(null);
|
||||
const [installingPluginId, setInstallingPluginId] = useState<string | null>(
|
||||
@@ -227,12 +227,12 @@ export default function AgentRunnerSelect({
|
||||
setCatalogLoading(true);
|
||||
setCatalogError(false);
|
||||
try {
|
||||
const catalog = await loadAgentRunnerCatalog();
|
||||
const catalog = await loadRunnerCatalog();
|
||||
setMarketplaceRunners(catalog.marketplaceRunners);
|
||||
setInstalledPluginIds(catalog.installedPluginIds);
|
||||
setInstalledPluginDescriptions(catalog.installedPluginDescriptions);
|
||||
} catch (error) {
|
||||
console.error('Failed to load AgentRunner catalog', error);
|
||||
console.error('Failed to load Runner catalog', error);
|
||||
setCatalogError(true);
|
||||
} finally {
|
||||
setCatalogLoading(false);
|
||||
@@ -245,9 +245,9 @@ export default function AgentRunnerSelect({
|
||||
|
||||
useEffect(() => {
|
||||
const syncPendingInstall = () =>
|
||||
setPendingInstall(readPendingAgentRunnerInstall(installScope));
|
||||
setPendingInstall(readPendingRunnerInstall(installScope));
|
||||
syncPendingInstall();
|
||||
return subscribePendingAgentRunnerInstall(installScope, syncPendingInstall);
|
||||
return subscribePendingRunnerInstall(installScope, syncPendingInstall);
|
||||
}, [installScope]);
|
||||
|
||||
const marketplaceOptions = useMemo(
|
||||
@@ -291,7 +291,7 @@ export default function AgentRunnerSelect({
|
||||
setInstallingPluginId(pluginId);
|
||||
setInstallError(null);
|
||||
try {
|
||||
const installed = await installMarketplaceAgentRunner(plugin, {
|
||||
const installed = await installMarketplaceRunner(plugin, {
|
||||
scope: installScope,
|
||||
onTaskCreated: (taskId) =>
|
||||
addTask({
|
||||
@@ -313,7 +313,7 @@ export default function AgentRunnerSelect({
|
||||
setInstallError(message);
|
||||
toast.error(message);
|
||||
} finally {
|
||||
const current = readPendingAgentRunnerInstall(installScope);
|
||||
const current = readPendingRunnerInstall(installScope);
|
||||
setPendingInstall(current);
|
||||
if (!current) setInstallingPluginId(null);
|
||||
}
|
||||
@@ -392,7 +392,7 @@ export default function AgentRunnerSelect({
|
||||
{t('agents.marketplaceRunners')}
|
||||
</span>
|
||||
<a
|
||||
href="https://space.langbot.app/market?type=plugin&component=AgentRunner"
|
||||
href="https://space.langbot.app/market?type=plugin&component=Runner"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="inline-flex shrink-0 items-center gap-1 rounded border px-1.5 py-0.5 text-[10px] font-medium text-foreground hover:bg-accent"
|
||||
+39
-39
@@ -6,38 +6,38 @@ import type { PipelineConfigTab } from '@/app/infra/entities/pipeline';
|
||||
import type { PluginV4 } from '@/app/infra/entities/plugin';
|
||||
import type { I18nObject } from '@/app/infra/entities/common';
|
||||
|
||||
export const RUNNER_COMPONENT_FILTER = 'AgentRunner';
|
||||
export const RUNNER_COMPONENT_FILTER = 'Runner';
|
||||
|
||||
const RUNNER_CATALOG_PAGE_SIZE = 100;
|
||||
const RUNNER_INSTALL_TIMEOUT_MS = 120_000;
|
||||
const RUNNER_REGISTRATION_TIMEOUT_MS = 60_000;
|
||||
const RUNNER_INSTALL_INTENT_KEY_PREFIX = 'langbot-agent-runner-install';
|
||||
const RUNNER_INSTALL_INTENT_EVENT = 'langbot-agent-runner-install-change';
|
||||
const RUNNER_INSTALL_INTENT_KEY_PREFIX = 'langbot-runner-install';
|
||||
const RUNNER_INSTALL_INTENT_EVENT = 'langbot-runner-install-change';
|
||||
|
||||
export type AgentRunnerMarketplaceErrorCode =
|
||||
export type RunnerMarketplaceErrorCode =
|
||||
| 'version-unavailable'
|
||||
| 'install-timeout'
|
||||
| 'registration-timeout';
|
||||
|
||||
export class AgentRunnerMarketplaceError extends Error {
|
||||
constructor(public readonly code: AgentRunnerMarketplaceErrorCode) {
|
||||
export class RunnerMarketplaceError extends Error {
|
||||
constructor(public readonly code: RunnerMarketplaceErrorCode) {
|
||||
super(code);
|
||||
this.name = 'AgentRunnerMarketplaceError';
|
||||
this.name = 'RunnerMarketplaceError';
|
||||
}
|
||||
}
|
||||
|
||||
export interface AgentRunnerCatalog {
|
||||
export interface RunnerCatalog {
|
||||
marketplaceRunners: PluginV4[];
|
||||
installedPluginIds: string[];
|
||||
installedPluginDescriptions: Record<string, I18nObject>;
|
||||
}
|
||||
|
||||
export interface InstalledAgentRunner {
|
||||
export interface InstalledRunner {
|
||||
configTab: PipelineConfigTab;
|
||||
runner: IDynamicFormItemOption;
|
||||
}
|
||||
|
||||
export interface PendingAgentRunnerInstall {
|
||||
export interface PendingRunnerInstall {
|
||||
taskId: number;
|
||||
pluginId: string;
|
||||
pluginAuthor: string;
|
||||
@@ -47,7 +47,7 @@ export interface PendingAgentRunnerInstall {
|
||||
startedAt: number;
|
||||
}
|
||||
|
||||
interface InstallAgentRunnerOptions {
|
||||
interface InstallRunnerOptions {
|
||||
scope: string;
|
||||
onTaskCreated?: (taskId: number) => void;
|
||||
}
|
||||
@@ -83,14 +83,14 @@ function emitInstallIntentChange(scope: string) {
|
||||
);
|
||||
}
|
||||
|
||||
export function readPendingAgentRunnerInstall(
|
||||
export function readPendingRunnerInstall(
|
||||
scope: string,
|
||||
): PendingAgentRunnerInstall | null {
|
||||
): PendingRunnerInstall | null {
|
||||
if (typeof window === 'undefined') return null;
|
||||
try {
|
||||
const raw = sessionStorage.getItem(installIntentStorageKey(scope));
|
||||
if (!raw) return null;
|
||||
const value = JSON.parse(raw) as Partial<PendingAgentRunnerInstall>;
|
||||
const value = JSON.parse(raw) as Partial<PendingRunnerInstall>;
|
||||
if (
|
||||
value.scope !== scope ||
|
||||
typeof value.taskId !== 'number' ||
|
||||
@@ -103,13 +103,13 @@ export function readPendingAgentRunnerInstall(
|
||||
sessionStorage.removeItem(installIntentStorageKey(scope));
|
||||
return null;
|
||||
}
|
||||
return value as PendingAgentRunnerInstall;
|
||||
return value as PendingRunnerInstall;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function writePendingAgentRunnerInstall(intent: PendingAgentRunnerInstall) {
|
||||
function writePendingRunnerInstall(intent: PendingRunnerInstall) {
|
||||
if (typeof window === 'undefined') return;
|
||||
sessionStorage.setItem(
|
||||
installIntentStorageKey(intent.scope),
|
||||
@@ -118,15 +118,15 @@ function writePendingAgentRunnerInstall(intent: PendingAgentRunnerInstall) {
|
||||
emitInstallIntentChange(intent.scope);
|
||||
}
|
||||
|
||||
export function clearPendingAgentRunnerInstall(scope: string, taskId?: number) {
|
||||
export function clearPendingRunnerInstall(scope: string, taskId?: number) {
|
||||
if (typeof window === 'undefined') return;
|
||||
const current = readPendingAgentRunnerInstall(scope);
|
||||
const current = readPendingRunnerInstall(scope);
|
||||
if (taskId !== undefined && current?.taskId !== taskId) return;
|
||||
sessionStorage.removeItem(installIntentStorageKey(scope));
|
||||
emitInstallIntentChange(scope);
|
||||
}
|
||||
|
||||
export function subscribePendingAgentRunnerInstall(
|
||||
export function subscribePendingRunnerInstall(
|
||||
scope: string,
|
||||
listener: () => void,
|
||||
) {
|
||||
@@ -140,7 +140,7 @@ export function subscribePendingAgentRunnerInstall(
|
||||
window.removeEventListener(RUNNER_INSTALL_INTENT_EVENT, handleChange);
|
||||
}
|
||||
|
||||
export async function loadAgentRunnerCatalog(): Promise<AgentRunnerCatalog> {
|
||||
export async function loadRunnerCatalog(): Promise<RunnerCatalog> {
|
||||
const cloudClient = await getCloudServiceClient();
|
||||
const [firstSearchResult, recommendationResult, installedResult] =
|
||||
await Promise.all([
|
||||
@@ -218,12 +218,12 @@ export async function loadAgentRunnerCatalog(): Promise<AgentRunnerCatalog> {
|
||||
};
|
||||
}
|
||||
|
||||
export async function installMarketplaceAgentRunner(
|
||||
export async function installMarketplaceRunner(
|
||||
plugin: PluginV4,
|
||||
options: InstallAgentRunnerOptions,
|
||||
): Promise<InstalledAgentRunner> {
|
||||
options: InstallRunnerOptions,
|
||||
): Promise<InstalledRunner> {
|
||||
if (!plugin.latest_version) {
|
||||
throw new AgentRunnerMarketplaceError('version-unavailable');
|
||||
throw new RunnerMarketplaceError('version-unavailable');
|
||||
}
|
||||
|
||||
const { task_id: taskId } = await httpClient.installPluginFromMarketplace(
|
||||
@@ -231,7 +231,7 @@ export async function installMarketplaceAgentRunner(
|
||||
plugin.name,
|
||||
plugin.latest_version,
|
||||
);
|
||||
const pending: PendingAgentRunnerInstall = {
|
||||
const pending: PendingRunnerInstall = {
|
||||
taskId,
|
||||
pluginId: marketplacePluginId(plugin),
|
||||
pluginAuthor: plugin.author,
|
||||
@@ -240,9 +240,9 @@ export async function installMarketplaceAgentRunner(
|
||||
scope: options.scope,
|
||||
startedAt: Date.now(),
|
||||
};
|
||||
writePendingAgentRunnerInstall(pending);
|
||||
writePendingRunnerInstall(pending);
|
||||
options.onTaskCreated?.(taskId);
|
||||
return finishAgentRunnerInstall(pending);
|
||||
return finishRunnerInstall(pending);
|
||||
}
|
||||
|
||||
function extractPluginLabel(plugin: PluginV4) {
|
||||
@@ -257,9 +257,9 @@ function extractPluginLabel(plugin: PluginV4) {
|
||||
return plugin.name;
|
||||
}
|
||||
|
||||
async function finishAgentRunnerInstall(
|
||||
pending: PendingAgentRunnerInstall,
|
||||
): Promise<InstalledAgentRunner> {
|
||||
async function finishRunnerInstall(
|
||||
pending: PendingRunnerInstall,
|
||||
): Promise<InstalledRunner> {
|
||||
// A refreshed page receives a fresh observation window. The backend task is
|
||||
// authoritative; `startedAt` is display metadata, not a reason to abandon a
|
||||
// still-running installation immediately after recovery.
|
||||
@@ -269,7 +269,7 @@ async function finishAgentRunnerInstall(
|
||||
const task = await httpClient.getAsyncTask(pending.taskId);
|
||||
if (task.runtime.done) {
|
||||
if (task.runtime.exception) {
|
||||
clearPendingAgentRunnerInstall(pending.scope, pending.taskId);
|
||||
clearPendingRunnerInstall(pending.scope, pending.taskId);
|
||||
throw new Error(task.runtime.exception);
|
||||
}
|
||||
installCompleted = true;
|
||||
@@ -279,7 +279,7 @@ async function finishAgentRunnerInstall(
|
||||
await wait(1000);
|
||||
}
|
||||
if (!installCompleted) {
|
||||
throw new AgentRunnerMarketplaceError('install-timeout');
|
||||
throw new RunnerMarketplaceError('install-timeout');
|
||||
}
|
||||
|
||||
const registrationDeadline = Date.now() + RUNNER_REGISTRATION_TIMEOUT_MS;
|
||||
@@ -303,20 +303,20 @@ async function finishAgentRunnerInstall(
|
||||
pluginRunnerOptions[0];
|
||||
|
||||
if (configTab && runner) {
|
||||
clearPendingAgentRunnerInstall(pending.scope, pending.taskId);
|
||||
clearPendingRunnerInstall(pending.scope, pending.taskId);
|
||||
return { configTab, runner };
|
||||
}
|
||||
await wait(1000);
|
||||
}
|
||||
|
||||
clearPendingAgentRunnerInstall(pending.scope, pending.taskId);
|
||||
throw new AgentRunnerMarketplaceError('registration-timeout');
|
||||
clearPendingRunnerInstall(pending.scope, pending.taskId);
|
||||
throw new RunnerMarketplaceError('registration-timeout');
|
||||
}
|
||||
|
||||
export async function resumePendingAgentRunnerInstall(
|
||||
export async function resumePendingRunnerInstall(
|
||||
scope: string,
|
||||
): Promise<InstalledAgentRunner | null> {
|
||||
const pending = readPendingAgentRunnerInstall(scope);
|
||||
): Promise<InstalledRunner | null> {
|
||||
const pending = readPendingRunnerInstall(scope);
|
||||
if (!pending) return null;
|
||||
return finishAgentRunnerInstall(pending);
|
||||
return finishRunnerInstall(pending);
|
||||
}
|
||||
@@ -582,10 +582,10 @@ export default function ToolResourceSelectors({
|
||||
|
||||
const sourceLabels = useMemo<Record<string, string>>(
|
||||
() => ({
|
||||
builtin: t('pipelines.agentRunner.builtinTools'),
|
||||
plugin: t('pipelines.agentRunner.pluginTools'),
|
||||
mcp: t('pipelines.agentRunner.mcpTools'),
|
||||
skill: t('pipelines.agentRunner.skillTools'),
|
||||
builtin: t('pipelines.runner.builtinTools'),
|
||||
plugin: t('pipelines.runner.pluginTools'),
|
||||
mcp: t('pipelines.runner.mcpTools'),
|
||||
skill: t('pipelines.runner.skillTools'),
|
||||
}),
|
||||
[t],
|
||||
);
|
||||
@@ -653,27 +653,27 @@ export default function ToolResourceSelectors({
|
||||
<div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<h3 className="text-sm font-semibold">
|
||||
{t('pipelines.agentRunner.toolsTitle')}
|
||||
{t('pipelines.runner.toolsTitle')}
|
||||
</h3>
|
||||
{pipelineId && (
|
||||
<InfoTooltip
|
||||
label={t('pipelines.agentRunner.toolsScopeTooltip')}
|
||||
label={t('pipelines.runner.toolsScopeTooltip')}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
{t('pipelines.agentRunner.toolsDescription')}
|
||||
{t('pipelines.runner.toolsDescription')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
<Label
|
||||
htmlFor="agent-runner-enable-all-tools"
|
||||
htmlFor="runner-enable-all-tools"
|
||||
className="cursor-pointer text-sm font-normal"
|
||||
>
|
||||
{t('pipelines.agentRunner.enableAllTools')}
|
||||
{t('pipelines.runner.enableAllTools')}
|
||||
</Label>
|
||||
<Switch
|
||||
id="agent-runner-enable-all-tools"
|
||||
id="runner-enable-all-tools"
|
||||
checked={enableAllTools}
|
||||
onCheckedChange={handleToggleToolMode}
|
||||
/>
|
||||
@@ -683,13 +683,13 @@ export default function ToolResourceSelectors({
|
||||
{enableAllTools ? (
|
||||
<div className="flex h-24 items-center justify-center rounded-lg border-2 border-dashed border-border bg-muted/30">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('pipelines.agentRunner.allToolsEnabled')}
|
||||
{t('pipelines.runner.allToolsEnabled')}
|
||||
</p>
|
||||
</div>
|
||||
) : selectedTools.length === 0 ? (
|
||||
<div className="flex h-24 items-center justify-center rounded-lg border-2 border-dashed border-border">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('pipelines.agentRunner.noToolsSelected')}
|
||||
{t('pipelines.runner.noToolsSelected')}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
@@ -764,7 +764,7 @@ export default function ToolResourceSelectors({
|
||||
}}
|
||||
>
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
{t('pipelines.agentRunner.editTools')}
|
||||
{t('pipelines.runner.editTools')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
@@ -773,10 +773,10 @@ export default function ToolResourceSelectors({
|
||||
<div className="space-y-4 rounded-lg border p-4">
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold">
|
||||
{t('pipelines.agentRunner.resourcesTitle')}
|
||||
{t('pipelines.runner.resourcesTitle')}
|
||||
</h3>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
{t('pipelines.agentRunner.resourcesDescription')}
|
||||
{t('pipelines.runner.resourcesDescription')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -785,7 +785,7 @@ export default function ToolResourceSelectors({
|
||||
<div className="flex items-center gap-2">
|
||||
<Database className="h-4 w-4 text-muted-foreground" />
|
||||
<span className="text-sm font-medium">
|
||||
{t('pipelines.agentRunner.knowledgeBases')}
|
||||
{t('pipelines.runner.knowledgeBases')}
|
||||
</span>
|
||||
</div>
|
||||
<Button
|
||||
@@ -855,26 +855,26 @@ export default function ToolResourceSelectors({
|
||||
<div className="flex items-center gap-2">
|
||||
<Server className="h-4 w-4 text-muted-foreground" />
|
||||
<span className="text-sm font-medium">
|
||||
{t('pipelines.agentRunner.mcpResources')}
|
||||
{t('pipelines.runner.mcpResources')}
|
||||
</span>
|
||||
{pipelineId && (
|
||||
<InfoTooltip
|
||||
label={t('pipelines.agentRunner.mcpResourcesScopeTooltip')}
|
||||
label={t('pipelines.runner.mcpResourcesScopeTooltip')}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Label
|
||||
htmlFor="agent-runner-mcp-resource-read"
|
||||
htmlFor="runner-mcp-resource-read"
|
||||
className="cursor-pointer text-sm font-normal"
|
||||
>
|
||||
{t('pipelines.agentRunner.enableMCPResourceRead')}
|
||||
{t('pipelines.runner.enableMCPResourceRead')}
|
||||
</Label>
|
||||
<InfoTooltip
|
||||
label={t('pipelines.agentRunner.mcpResourceReadTooltip')}
|
||||
label={t('pipelines.runner.mcpResourceReadTooltip')}
|
||||
/>
|
||||
<Switch
|
||||
id="agent-runner-mcp-resource-read"
|
||||
id="runner-mcp-resource-read"
|
||||
checked={mcpResourceReadEnabled}
|
||||
onCheckedChange={(checked) =>
|
||||
onChange({ 'mcp-resource-agent-read-enabled': checked })
|
||||
@@ -886,7 +886,7 @@ export default function ToolResourceSelectors({
|
||||
{resourceServers.length === 0 ? (
|
||||
<div className="flex h-20 items-center justify-center rounded-lg border-2 border-dashed border-border">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('pipelines.agentRunner.noMCPResourcesAvailable')}
|
||||
{t('pipelines.runner.noMCPResourcesAvailable')}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
@@ -956,9 +956,7 @@ export default function ToolResourceSelectors({
|
||||
<Dialog open={toolsDialogOpen} onOpenChange={setToolsDialogOpen}>
|
||||
<DialogContent className="flex max-h-[80vh] max-w-2xl flex-col overflow-hidden">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{t('pipelines.agentRunner.selectTools')}
|
||||
</DialogTitle>
|
||||
<DialogTitle>{t('pipelines.runner.selectTools')}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex-1 space-y-5 overflow-y-auto pr-2">
|
||||
{availableToolGroups.map((sourceGroup) => {
|
||||
@@ -970,16 +968,12 @@ export default function ToolResourceSelectors({
|
||||
</span>
|
||||
{pipelineId && sourceGroup.key === 'mcp' && (
|
||||
<InfoTooltip
|
||||
label={t(
|
||||
'pipelines.agentRunner.mcpToolsScopeTooltip',
|
||||
)}
|
||||
label={t('pipelines.runner.mcpToolsScopeTooltip')}
|
||||
/>
|
||||
)}
|
||||
{sourceGroup.key === 'skill' && (
|
||||
<InfoTooltip
|
||||
label={t(
|
||||
'pipelines.agentRunner.skillToolsScopeTooltip',
|
||||
)}
|
||||
label={t('pipelines.runner.skillToolsScopeTooltip')}
|
||||
/>
|
||||
)}
|
||||
<Badge variant="outline" className="ml-auto">
|
||||
@@ -1047,7 +1041,7 @@ export default function ToolResourceSelectors({
|
||||
{availableToolGroups.length === 0 && (
|
||||
<div className="flex h-24 items-center justify-center rounded-lg border-2 border-dashed border-border">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('pipelines.agentRunner.noToolsSelected')}
|
||||
{t('pipelines.runner.noToolsSelected')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -1072,7 +1066,7 @@ export default function ToolResourceSelectors({
|
||||
<DialogContent className="flex max-h-[80vh] max-w-2xl flex-col overflow-hidden">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{t('pipelines.agentRunner.selectKnowledgeBases')}
|
||||
{t('pipelines.runner.selectKnowledgeBases')}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex-1 space-y-2 overflow-y-auto pr-2">
|
||||
|
||||
@@ -58,13 +58,13 @@ import {
|
||||
Copy,
|
||||
} from 'lucide-react';
|
||||
import PipelineExtension from '@/app/home/pipelines/components/pipeline-extensions/PipelineExtension';
|
||||
import AgentRunnerSelect from '@/app/home/agents/components/AgentRunnerSelect';
|
||||
import RunnerSelect from '@/app/home/agents/components/RunnerSelect';
|
||||
import {
|
||||
getErrorMessage,
|
||||
readPendingAgentRunnerInstall,
|
||||
resumePendingAgentRunnerInstall,
|
||||
type InstalledAgentRunner,
|
||||
} from '@/app/home/agents/agent-runner-marketplace';
|
||||
readPendingRunnerInstall,
|
||||
resumePendingRunnerInstall,
|
||||
type InstalledRunner,
|
||||
} from '@/app/home/agents/runner-marketplace';
|
||||
|
||||
interface PipelineFormComponentProps {
|
||||
pipelineId?: string;
|
||||
@@ -229,12 +229,9 @@ const PipelineFormComponent = forwardRef<
|
||||
},
|
||||
});
|
||||
const runnerInstallScope = `pipeline:${pipelineId || 'new'}`;
|
||||
const applyInstalledRunner = useCallback(
|
||||
(installed: InstalledAgentRunner) => {
|
||||
setAIConfigTabSchema(installed.configTab);
|
||||
},
|
||||
[],
|
||||
);
|
||||
const applyInstalledRunner = useCallback((installed: InstalledRunner) => {
|
||||
setAIConfigTabSchema(installed.configTab);
|
||||
}, []);
|
||||
const dynamicFormSystemContext = useMemo(
|
||||
() => ({ pipeline_id: pipelineId }),
|
||||
[pipelineId],
|
||||
@@ -307,12 +304,12 @@ const PipelineFormComponent = forwardRef<
|
||||
if (
|
||||
!metadataLoaded ||
|
||||
!pipelineLoaded ||
|
||||
!readPendingAgentRunnerInstall(runnerInstallScope)
|
||||
!readPendingRunnerInstall(runnerInstallScope)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
void resumePendingAgentRunnerInstall(runnerInstallScope)
|
||||
void resumePendingRunnerInstall(runnerInstallScope)
|
||||
.then((installed) => {
|
||||
if (cancelled || !installed) return;
|
||||
applyInstalledRunner(installed);
|
||||
@@ -575,7 +572,7 @@ const PipelineFormComponent = forwardRef<
|
||||
systemContext={dynamicFormSystemContext}
|
||||
renderItem={({ config, field }) =>
|
||||
config.name === 'id' ? (
|
||||
<AgentRunnerSelect
|
||||
<RunnerSelect
|
||||
options={config.options ?? []}
|
||||
label={extractI18nObject(config.label)}
|
||||
value={String(field.value ?? '')}
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
FileText,
|
||||
PanelTop,
|
||||
Bot,
|
||||
Zap,
|
||||
} from 'lucide-react';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
|
||||
@@ -33,8 +32,7 @@ export default function PluginComponentList({
|
||||
KnowledgeEngine: <Book className="w-5 h-5" />,
|
||||
Parser: <FileText className="w-5 h-5" />,
|
||||
Page: <PanelTop className="w-5 h-5" />,
|
||||
AgentRunner: <Bot className="w-5 h-5" />,
|
||||
EventProcessor: <Zap className="w-5 h-5" />,
|
||||
Runner: <Bot className="w-5 h-5" />,
|
||||
};
|
||||
|
||||
const componentKindList = Object.keys(components || {});
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
Book,
|
||||
FileText,
|
||||
Hash,
|
||||
Zap,
|
||||
Wrench,
|
||||
type LucideIcon,
|
||||
} from 'lucide-react';
|
||||
@@ -17,6 +16,5 @@ export const pluginComponentIconMap: Record<string, LucideIcon> = {
|
||||
KnowledgeEngine: Book,
|
||||
Parser: FileText,
|
||||
Page: AppWindow,
|
||||
AgentRunner: Bot,
|
||||
EventProcessor: Zap,
|
||||
Runner: Bot,
|
||||
};
|
||||
|
||||
@@ -18,7 +18,6 @@ import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
|
||||
import {
|
||||
Search,
|
||||
Puzzle,
|
||||
Zap,
|
||||
Server,
|
||||
Sparkles,
|
||||
Wrench,
|
||||
@@ -72,8 +71,7 @@ const MARKET_COMPONENT_VALUES = [
|
||||
'KnowledgeEngine',
|
||||
'Parser',
|
||||
'Page',
|
||||
'AgentRunner',
|
||||
'EventProcessor',
|
||||
'Runner',
|
||||
];
|
||||
|
||||
function getComponentFilterFromQuery(
|
||||
@@ -242,15 +240,10 @@ function MarketPageContent({
|
||||
icon: AppWindow,
|
||||
},
|
||||
{
|
||||
value: 'AgentRunner',
|
||||
label: t('market.componentName.AgentRunner'),
|
||||
value: 'Runner',
|
||||
label: t('market.componentName.Runner'),
|
||||
icon: Bot,
|
||||
},
|
||||
{
|
||||
value: 'EventProcessor',
|
||||
label: t('market.componentName.EventProcessor'),
|
||||
icon: Zap,
|
||||
},
|
||||
];
|
||||
|
||||
// 获取当前排序参数
|
||||
|
||||
Reference in New Issue
Block a user