mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-09 20:50:58 +00:00
feat(agent-runner): add plugin runner host integration
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
DynamicFormItemType,
|
||||
IDynamicFormItemSchema,
|
||||
SYSTEM_FIELD_PREFIX,
|
||||
DynamicFormItemType,
|
||||
} from '@/app/infra/entities/form/dynamic';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
} from '@/components/ui/tooltip';
|
||||
import { systemInfo } from '@/app/infra/http';
|
||||
import { getAdapterDocUrl } from '@/app/infra/entities/adapter-docs';
|
||||
import { parseDynamicFormItemType } from './DynamicFormItemConfig';
|
||||
|
||||
/**
|
||||
* Resolve the value referenced by a `show_if.field` string.
|
||||
@@ -111,7 +112,7 @@ function getValueSchema(spec: DynamicFormValueSpec) {
|
||||
return z.array(z.any());
|
||||
}
|
||||
|
||||
switch (spec.type) {
|
||||
switch (normalizeItemType(spec.type)) {
|
||||
case DynamicFormItemType.INT:
|
||||
return z.number();
|
||||
case DynamicFormItemType.FLOAT:
|
||||
@@ -435,6 +436,13 @@ function DisabledTooltipIcon({ text }: { text: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize plugin manifest type names to frontend-compatible types
|
||||
*/
|
||||
function normalizeItemType(type: string): DynamicFormItemType {
|
||||
return parseDynamicFormItemType(type);
|
||||
}
|
||||
|
||||
export default function DynamicFormComponent({
|
||||
itemConfigList,
|
||||
onSubmit,
|
||||
@@ -686,7 +694,14 @@ export default function DynamicFormComponent({
|
||||
}}
|
||||
/>
|
||||
|
||||
{itemConfigList.map((config) => {
|
||||
{itemConfigList.map((config, index) => {
|
||||
// Create a normalized config with type converted to frontend format
|
||||
const normalizedConfig = {
|
||||
...config,
|
||||
type: normalizeItemType(config.type),
|
||||
};
|
||||
const fieldKey = config.id || config.name || `field-${index}`;
|
||||
|
||||
if (config.show_if) {
|
||||
const dependValue = resolveShowIfValue(
|
||||
config.show_if.field,
|
||||
@@ -781,7 +796,7 @@ export default function DynamicFormComponent({
|
||||
}
|
||||
|
||||
// Webhook URL fields are display-only; render outside of form binding
|
||||
if (config.type === 'webhook-url') {
|
||||
if (normalizedConfig.type === 'webhook-url') {
|
||||
const webhookUrl = (systemContext?.webhook_url as string) || '';
|
||||
const extraWebhookUrl =
|
||||
(systemContext?.extra_webhook_url as string) || '';
|
||||
@@ -790,7 +805,7 @@ export default function DynamicFormComponent({
|
||||
|
||||
return (
|
||||
<WebhookUrlField
|
||||
key={config.id}
|
||||
key={fieldKey}
|
||||
label={extractI18nObject(config.label)}
|
||||
description={
|
||||
config.description
|
||||
@@ -803,7 +818,7 @@ export default function DynamicFormComponent({
|
||||
);
|
||||
}
|
||||
|
||||
if (config.type === 'embed-code') {
|
||||
if (normalizedConfig.type === 'embed-code') {
|
||||
const botUuid = (systemContext?.bot_uuid as string) || '';
|
||||
if (!botUuid) return null;
|
||||
|
||||
@@ -821,7 +836,7 @@ export default function DynamicFormComponent({
|
||||
|
||||
return (
|
||||
<EmbedCodeField
|
||||
key={config.id}
|
||||
key={fieldKey}
|
||||
label={extractI18nObject(config.label)}
|
||||
description={
|
||||
config.description
|
||||
@@ -860,7 +875,7 @@ export default function DynamicFormComponent({
|
||||
// QR code login button (e.g. Feishu one-click create, WeChat scan login)
|
||||
if (config.type === 'qr-code-login') {
|
||||
return (
|
||||
<FormItem key={config.id}>
|
||||
<FormItem key={fieldKey}>
|
||||
<div
|
||||
className="relative flex items-center gap-4 p-4 rounded-xl border-2 border-dashed cursor-pointer transition-all hover:border-solid hover:shadow-md group"
|
||||
style={{
|
||||
@@ -953,10 +968,10 @@ export default function DynamicFormComponent({
|
||||
}
|
||||
|
||||
// Boolean fields use a special inline layout
|
||||
if (config.type === 'boolean') {
|
||||
if (normalizedConfig.type === 'boolean') {
|
||||
return (
|
||||
<FormField
|
||||
key={config.id}
|
||||
key={fieldKey}
|
||||
control={form.control}
|
||||
name={config.name as keyof FormValues}
|
||||
render={({ field }) => (
|
||||
@@ -980,7 +995,7 @@ export default function DynamicFormComponent({
|
||||
</div>
|
||||
<FormControl>
|
||||
<DynamicFormItemComponent
|
||||
config={config}
|
||||
config={normalizedConfig}
|
||||
field={field}
|
||||
formValues={watchedValues as Record<string, unknown>}
|
||||
onFileUploaded={onFileUploaded}
|
||||
@@ -998,7 +1013,7 @@ export default function DynamicFormComponent({
|
||||
|
||||
return (
|
||||
<FormField
|
||||
key={config.id}
|
||||
key={fieldKey}
|
||||
control={form.control}
|
||||
name={config.name as keyof FormValues}
|
||||
render={({ field }) => (
|
||||
@@ -1020,7 +1035,7 @@ export default function DynamicFormComponent({
|
||||
)}
|
||||
>
|
||||
<DynamicFormItemComponent
|
||||
config={config}
|
||||
config={normalizedConfig}
|
||||
field={field}
|
||||
formValues={watchedValues as Record<string, unknown>}
|
||||
onFileUploaded={onFileUploaded}
|
||||
|
||||
@@ -289,11 +289,13 @@ export default function DynamicFormItemComponent({
|
||||
switch (config.type) {
|
||||
case DynamicFormItemType.INT:
|
||||
case DynamicFormItemType.FLOAT:
|
||||
case DynamicFormItemType.NUMBER:
|
||||
return (
|
||||
<Input
|
||||
type="number"
|
||||
className="w-full max-w-xs"
|
||||
{...field}
|
||||
value={field.value ?? ''}
|
||||
onChange={(e) => field.onChange(Number(e.target.value))}
|
||||
/>
|
||||
);
|
||||
@@ -302,7 +304,11 @@ export default function DynamicFormItemComponent({
|
||||
if (config.options && config.options.length > 0) {
|
||||
return (
|
||||
<div className="flex w-full max-w-md min-w-0 items-center gap-1.5">
|
||||
<Input className="min-w-0 flex-1" {...field} />
|
||||
<Input
|
||||
className="min-w-0 flex-1"
|
||||
{...field}
|
||||
value={field.value ?? ''}
|
||||
/>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
@@ -333,18 +339,37 @@ export default function DynamicFormItemComponent({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <Input className="w-full max-w-md" {...field} />;
|
||||
return (
|
||||
<Input
|
||||
className="w-full max-w-md"
|
||||
{...field}
|
||||
value={field.value ?? ''}
|
||||
/>
|
||||
);
|
||||
|
||||
case DynamicFormItemType.TEXT:
|
||||
return (
|
||||
<Textarea
|
||||
{...field}
|
||||
value={field.value ?? ''}
|
||||
className="min-h-[120px] w-full max-w-full resize-y overflow-x-hidden break-all"
|
||||
/>
|
||||
);
|
||||
|
||||
case DynamicFormItemType.JSON:
|
||||
return (
|
||||
<Textarea
|
||||
{...field}
|
||||
value={field.value ?? ''}
|
||||
className="min-h-[200px] font-mono text-sm"
|
||||
placeholder='{"key": "value"}'
|
||||
/>
|
||||
);
|
||||
|
||||
case DynamicFormItemType.BOOLEAN:
|
||||
return <Switch checked={field.value} onCheckedChange={field.onChange} />;
|
||||
return (
|
||||
<Switch checked={!!field.value} onCheckedChange={field.onChange} />
|
||||
);
|
||||
|
||||
case DynamicFormItemType.STRING_ARRAY:
|
||||
return (
|
||||
@@ -1641,7 +1666,7 @@ export default function DynamicFormItemComponent({
|
||||
{/* 内容输入 */}
|
||||
<Textarea
|
||||
className="min-h-20 w-full min-w-0 flex-1 resize-y overflow-x-hidden break-all sm:w-[300px]"
|
||||
value={item.content}
|
||||
value={item.content ?? ''}
|
||||
onChange={(e) => {
|
||||
const newValue = [...(field.value ?? promptItems)];
|
||||
newValue[index] = {
|
||||
|
||||
@@ -50,6 +50,18 @@ export function isDynamicFormItemType(
|
||||
}
|
||||
|
||||
export function parseDynamicFormItemType(value: string): DynamicFormItemType {
|
||||
const typeMap: Record<string, DynamicFormItemType> = {
|
||||
[DynamicFormItemType.SELECT_LLM_MODEL]:
|
||||
DynamicFormItemType.LLM_MODEL_SELECTOR,
|
||||
[DynamicFormItemType.SELECT_KNOWLEDGE_BASES]:
|
||||
DynamicFormItemType.KNOWLEDGE_BASE_MULTI_SELECTOR,
|
||||
[DynamicFormItemType.NUMBER]: DynamicFormItemType.FLOAT,
|
||||
[DynamicFormItemType.JSON]: DynamicFormItemType.TEXT,
|
||||
};
|
||||
if (value in typeMap) {
|
||||
return typeMap[value];
|
||||
}
|
||||
|
||||
return isDynamicFormItemType(value) ? value : DynamicFormItemType.UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user