@open-mercato/ai-assistant 0.6.7 → 0.6.8-develop.6874.1.982d6097d8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +12 -29
- package/dist/modules/ai_assistant/__integration__/TC-AI-MODERATION-008-settings-and-audit.spec.js +80 -0
- package/dist/modules/ai_assistant/__integration__/TC-AI-MODERATION-008-settings-and-audit.spec.js.map +7 -0
- package/dist/modules/ai_assistant/__integration__/TC-AI-MODERATION-009-ui.spec.js +124 -0
- package/dist/modules/ai_assistant/__integration__/TC-AI-MODERATION-009-ui.spec.js.map +7 -0
- package/dist/modules/ai_assistant/__integration__/helpers/aiAssistantFixtures.js +30 -0
- package/dist/modules/ai_assistant/__integration__/helpers/aiAssistantFixtures.js.map +2 -2
- package/dist/modules/ai_assistant/api/ai/chat/route.js +7 -0
- package/dist/modules/ai_assistant/api/ai/chat/route.js.map +2 -2
- package/dist/modules/ai_assistant/api/moderation-flags/route.js +121 -0
- package/dist/modules/ai_assistant/api/moderation-flags/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/settings/allowlist/route.js +10 -10
- package/dist/modules/ai_assistant/api/settings/allowlist/route.js.map +2 -2
- package/dist/modules/ai_assistant/api/settings/route.js +34 -13
- package/dist/modules/ai_assistant/api/settings/route.js.map +2 -2
- package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.js +121 -15
- package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.js.map +2 -2
- package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.js +2 -2
- package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.js.map +2 -2
- package/dist/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/AiModerationFlagsPageClient.js +139 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/AiModerationFlagsPageClient.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/page.js +10 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/page.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/page.meta.js +26 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/page.meta.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js +4 -4
- package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js.map +2 -2
- package/dist/modules/ai_assistant/data/entities.js +42 -0
- package/dist/modules/ai_assistant/data/entities.js.map +2 -2
- package/dist/modules/ai_assistant/data/repositories/AiAgentRuntimeOverrideRepository.js +3 -1
- package/dist/modules/ai_assistant/data/repositories/AiAgentRuntimeOverrideRepository.js.map +2 -2
- package/dist/modules/ai_assistant/data/repositories/AiModerationFlagRepository.js +44 -0
- package/dist/modules/ai_assistant/data/repositories/AiModerationFlagRepository.js.map +7 -0
- package/dist/modules/ai_assistant/di.js +6 -2
- package/dist/modules/ai_assistant/di.js.map +2 -2
- package/dist/modules/ai_assistant/events.js +6 -0
- package/dist/modules/ai_assistant/events.js.map +2 -2
- package/dist/modules/ai_assistant/i18n/de.json +42 -0
- package/dist/modules/ai_assistant/i18n/en.json +42 -0
- package/dist/modules/ai_assistant/i18n/es.json +42 -0
- package/dist/modules/ai_assistant/i18n/ko.json +527 -0
- package/dist/modules/ai_assistant/i18n/pl.json +42 -0
- package/dist/modules/ai_assistant/lib/agent-runtime.js +158 -5
- package/dist/modules/ai_assistant/lib/agent-runtime.js.map +2 -2
- package/dist/modules/ai_assistant/lib/ai-agent-definition.js.map +2 -2
- package/dist/modules/ai_assistant/lib/auth.js +6 -7
- package/dist/modules/ai_assistant/lib/auth.js.map +2 -2
- package/dist/modules/ai_assistant/lib/entity-graph.js +3 -3
- package/dist/modules/ai_assistant/lib/entity-graph.js.map +2 -2
- package/dist/modules/ai_assistant/lib/llm-adapters/anthropic.js +3 -0
- package/dist/modules/ai_assistant/lib/llm-adapters/anthropic.js.map +2 -2
- package/dist/modules/ai_assistant/lib/llm-adapters/openai.js +8 -1
- package/dist/modules/ai_assistant/lib/llm-adapters/openai.js.map +2 -2
- package/dist/modules/ai_assistant/lib/model-factory.js +6 -1
- package/dist/modules/ai_assistant/lib/model-factory.js.map +2 -2
- package/dist/modules/ai_assistant/lib/moderation-flag-recorder.js +43 -0
- package/dist/modules/ai_assistant/lib/moderation-flag-recorder.js.map +7 -0
- package/dist/modules/ai_assistant/lib/moderation-policy.js +24 -0
- package/dist/modules/ai_assistant/lib/moderation-policy.js.map +7 -0
- package/dist/modules/ai_assistant/lib/moderation.js +109 -0
- package/dist/modules/ai_assistant/lib/moderation.js.map +7 -0
- package/dist/modules/ai_assistant/lib/openai-compatible-presets.js +2 -0
- package/dist/modules/ai_assistant/lib/openai-compatible-presets.js.map +2 -2
- package/dist/modules/ai_assistant/lib/tool-search.js +4 -12
- package/dist/modules/ai_assistant/lib/tool-search.js.map +2 -2
- package/dist/modules/ai_assistant/migrations/Migration20260610134045_ai_assistant.js +19 -0
- package/dist/modules/ai_assistant/migrations/Migration20260610134045_ai_assistant.js.map +7 -0
- package/generated/entities/ai_agent_runtime_override/index.ts +1 -0
- package/generated/entities/ai_moderation_flag/index.ts +9 -0
- package/generated/entities.ids.generated.ts +2 -1
- package/generated/entity-fields-registry.ts +13 -1
- package/package.json +13 -12
- package/src/modules/ai_assistant/__integration__/TC-AI-MODERATION-008-settings-and-audit.spec.ts +126 -0
- package/src/modules/ai_assistant/__integration__/TC-AI-MODERATION-009-ui.spec.ts +161 -0
- package/src/modules/ai_assistant/__integration__/helpers/aiAssistantFixtures.ts +48 -0
- package/src/modules/ai_assistant/api/ai/chat/route.ts +10 -0
- package/src/modules/ai_assistant/api/moderation-flags/__tests__/route.test.ts +122 -0
- package/src/modules/ai_assistant/api/moderation-flags/route.ts +137 -0
- package/src/modules/ai_assistant/api/settings/__tests__/route.test.ts +58 -8
- package/src/modules/ai_assistant/api/settings/allowlist/route.ts +10 -12
- package/src/modules/ai_assistant/api/settings/route.ts +33 -10
- package/src/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.tsx +161 -15
- package/src/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.tsx +2 -2
- package/src/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/AiModerationFlagsPageClient.tsx +186 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/page.meta.ts +24 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/page.tsx +12 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.tsx +4 -4
- package/src/modules/ai_assistant/data/entities.ts +56 -0
- package/src/modules/ai_assistant/data/repositories/AiAgentRuntimeOverrideRepository.ts +4 -0
- package/src/modules/ai_assistant/data/repositories/AiModerationFlagRepository.ts +79 -0
- package/src/modules/ai_assistant/di.ts +5 -1
- package/src/modules/ai_assistant/events.ts +21 -0
- package/src/modules/ai_assistant/i18n/de.json +42 -0
- package/src/modules/ai_assistant/i18n/en.json +42 -0
- package/src/modules/ai_assistant/i18n/es.json +42 -0
- package/src/modules/ai_assistant/i18n/ko.json +527 -0
- package/src/modules/ai_assistant/i18n/pl.json +42 -0
- package/src/modules/ai_assistant/lib/__tests__/auth.test.ts +2 -5
- package/src/modules/ai_assistant/lib/__tests__/input-moderation-gate.test.ts +121 -0
- package/src/modules/ai_assistant/lib/__tests__/llm-adapters-anthropic.test.ts +13 -0
- package/src/modules/ai_assistant/lib/__tests__/llm-adapters-openai.test.ts +41 -0
- package/src/modules/ai_assistant/lib/__tests__/model-factory.test.ts +60 -0
- package/src/modules/ai_assistant/lib/__tests__/moderation-flag-recorder.test.ts +76 -0
- package/src/modules/ai_assistant/lib/__tests__/moderation-policy.test.ts +86 -0
- package/src/modules/ai_assistant/lib/__tests__/moderation.test.ts +114 -0
- package/src/modules/ai_assistant/lib/agent-runtime.ts +259 -0
- package/src/modules/ai_assistant/lib/ai-agent-definition.ts +10 -0
- package/src/modules/ai_assistant/lib/auth.ts +6 -15
- package/src/modules/ai_assistant/lib/entity-graph.ts +3 -3
- package/src/modules/ai_assistant/lib/llm-adapters/anthropic.ts +10 -0
- package/src/modules/ai_assistant/lib/llm-adapters/openai.ts +32 -1
- package/src/modules/ai_assistant/lib/model-factory.ts +33 -0
- package/src/modules/ai_assistant/lib/moderation-flag-recorder.ts +75 -0
- package/src/modules/ai_assistant/lib/moderation-policy.ts +77 -0
- package/src/modules/ai_assistant/lib/moderation.ts +179 -0
- package/src/modules/ai_assistant/lib/openai-compatible-presets.ts +2 -0
- package/src/modules/ai_assistant/lib/tool-search.ts +4 -17
- package/src/modules/ai_assistant/migrations/.snapshot-open-mercato.json +388 -16
- package/src/modules/ai_assistant/migrations/Migration20260610134045_ai_assistant.ts +21 -0
|
@@ -73,7 +73,7 @@ function AiTenantAllowlistPageClient() {
|
|
|
73
73
|
if (settingsQuery.isError || !settingsQuery.data) {
|
|
74
74
|
return /* @__PURE__ */ jsxs("div", { className: "flex max-w-3xl flex-col gap-4", children: [
|
|
75
75
|
pageHeader,
|
|
76
|
-
/* @__PURE__ */ jsxs(Alert, {
|
|
76
|
+
/* @__PURE__ */ jsxs(Alert, { status: "error", children: [
|
|
77
77
|
/* @__PURE__ */ jsx(AlertTitle, { children: t("ai_assistant.allowlist.loadError.title", "Failed to load allowlist") }),
|
|
78
78
|
/* @__PURE__ */ jsx(AlertDescription, { children: settingsQuery.error instanceof Error ? settingsQuery.error.message : t("ai_assistant.allowlist.loadError.body", "Try refreshing the page.") })
|
|
79
79
|
] })
|
|
@@ -229,7 +229,7 @@ function AiTenantAllowlistPageClient() {
|
|
|
229
229
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6 max-w-3xl", children: [
|
|
230
230
|
pageHeader,
|
|
231
231
|
envBanner,
|
|
232
|
-
feedback ? /* @__PURE__ */ jsx(Alert, {
|
|
232
|
+
feedback ? /* @__PURE__ */ jsx(Alert, { status: feedback.kind === "error" ? "error" : void 0, children: /* @__PURE__ */ jsx(AlertDescription, { children: feedback.text }) }) : null,
|
|
233
233
|
/* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-6 space-y-6", children: [
|
|
234
234
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
235
235
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { Loader2, Save, Shield, Trash2, ShieldCheck } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\n\ntype EnvAllowlistConfig = {\n providers: string[] | null\n modelsByProvider: Record<string, string[]>\n hasRestrictions: boolean\n}\n\ntype TenantAllowlist = {\n allowedProviders: string[] | null\n allowedModelsByProvider: Record<string, string[]>\n}\n\ntype EffectiveAllowlist = {\n providers: string[] | null\n modelsByProvider: Record<string, string[]>\n hasRestrictions: boolean\n tenantOverridesActive: boolean\n}\n\ntype ProviderEntry = {\n id: string\n name: string\n defaultModel: string\n envKey: string | null\n configured: boolean\n defaultModels: Array<{ id: string; name: string; contextWindow?: number; tags?: string[] }>\n}\n\ntype SettingsResponse = {\n availableProviders: ProviderEntry[]\n allowlistProviders?: ProviderEntry[]\n allowlist: EnvAllowlistConfig\n tenantAllowlist: TenantAllowlist | null\n effectiveAllowlist: EffectiveAllowlist\n}\n\nasync function fetchSettings(): Promise<SettingsResponse> {\n const { result, status } = await apiCallOrThrow<SettingsResponse>(\n '/api/ai_assistant/settings',\n { method: 'GET', credentials: 'include' },\n { errorMessage: 'Failed to load AI settings' },\n )\n if (!result) throw new Error(`Failed to load settings (${status})`)\n return result\n}\n\ntype EditState = {\n /** null = \"no tenant restriction (inherit env)\"; array = explicit tenant pick */\n allowedProviders: string[] | null\n allowedModelsByProvider: Record<string, string[]>\n}\n\nfunction snapshotToEditState(snapshot: TenantAllowlist | null): EditState {\n return {\n allowedProviders: snapshot?.allowedProviders ?? null,\n allowedModelsByProvider: { ...(snapshot?.allowedModelsByProvider ?? {}) },\n }\n}\n\nexport function AiTenantAllowlistPageClient(): React.JSX.Element {\n const t = useT()\n const queryClient = useQueryClient()\n const settingsQuery = useQuery({ queryKey: ['ai_assistant', 'settings'], queryFn: fetchSettings, staleTime: 0 })\n\n const [editState, setEditState] = React.useState<EditState>({\n allowedProviders: null,\n allowedModelsByProvider: {},\n })\n const [dirty, setDirty] = React.useState(false)\n const [saving, setSaving] = React.useState(false)\n const [clearing, setClearing] = React.useState(false)\n const [feedback, setFeedback] = React.useState<{ kind: 'ok' | 'error'; text: string } | null>(null)\n const { runMutation: runSaveAllowlistMutation } = useGuardedMutation({\n contextId: 'ai-tenant-allowlist-save',\n })\n const { runMutation: runClearAllowlistMutation } = useGuardedMutation({\n contextId: 'ai-tenant-allowlist-clear',\n })\n\n React.useEffect(() => {\n if (settingsQuery.data) {\n setEditState(snapshotToEditState(settingsQuery.data.tenantAllowlist))\n setDirty(false)\n }\n }, [settingsQuery.data])\n\n const pageHeader = (\n <div className=\"space-y-1\">\n <h1 className=\"flex items-center gap-2 text-2xl font-bold\">\n <Shield className=\"size-6\" />\n {t('ai_assistant.allowlist.title', 'AI provider & model allowlist')}\n </h1>\n <p className=\"text-muted-foreground\">\n {t(\n 'ai_assistant.allowlist.subtitle',\n 'Limit which providers and models the runtime, settings, and chat picker may use for this tenant. The env allowlist is the outer constraint \u2014 tenant picks narrow it further.',\n )}\n </p>\n </div>\n )\n\n if (settingsQuery.isLoading) {\n return (\n <div className=\"flex max-w-3xl flex-col gap-4\">\n {pageHeader}\n <div className=\"flex w-fit items-center gap-2 rounded-md border border-border bg-card px-3 py-2 text-sm text-muted-foreground\" role=\"status\">\n <Loader2 className=\"size-4 animate-spin\" />\n {t('ai_assistant.allowlist.loading', 'Loading allowlist\u2026')}\n </div>\n </div>\n )\n }\n\n if (settingsQuery.isError || !settingsQuery.data) {\n return (\n <div className=\"flex max-w-3xl flex-col gap-4\">\n {pageHeader}\n <Alert variant=\"destructive\">\n <AlertTitle>{t('ai_assistant.allowlist.loadError.title', 'Failed to load allowlist')}</AlertTitle>\n <AlertDescription>\n {settingsQuery.error instanceof Error\n ? settingsQuery.error.message\n : t('ai_assistant.allowlist.loadError.body', 'Try refreshing the page.')}\n </AlertDescription>\n </Alert>\n </div>\n )\n }\n\n const settings = settingsQuery.data\n const envAllowedProviders = settings.allowlist.providers\n const envModelsByProvider = settings.allowlist.modelsByProvider\n\n // Provider universe to render: env-allowed providers (or all configured if env unset).\n const editableProviders = settings.allowlistProviders ?? settings.availableProviders\n const candidateProviders = editableProviders.filter((p) => {\n if (envAllowedProviders === null) return true\n return envAllowedProviders.some((id) => id.toLowerCase() === p.id.toLowerCase())\n })\n\n const tenantPickedProviders = editState.allowedProviders\n const isProviderEnabled = (id: string): boolean => {\n if (tenantPickedProviders === null) return true\n return tenantPickedProviders.includes(id)\n }\n\n const toggleProvider = (id: string, next: boolean): void => {\n setDirty(true)\n setFeedback(null)\n setEditState((prev) => {\n const current = prev.allowedProviders\n if (next) {\n const list = current === null ? [id] : Array.from(new Set([...current, id]))\n return { ...prev, allowedProviders: list }\n }\n const list = current === null\n ? candidateProviders.map((p) => p.id).filter((pid) => pid !== id)\n : current.filter((pid) => pid !== id)\n return { ...prev, allowedProviders: list }\n })\n }\n\n const isModelEnabled = (providerId: string, modelId: string): boolean => {\n const list = editState.allowedModelsByProvider[providerId]\n if (list === undefined) return true\n return list.includes(modelId)\n }\n\n const toggleModel = (providerId: string, modelId: string, next: boolean): void => {\n setDirty(true)\n setFeedback(null)\n const provider = candidateProviders.find((p) => p.id === providerId)\n const allModelIds = provider?.defaultModels.map((m) => m.id) ?? []\n setEditState((prev) => {\n const current = prev.allowedModelsByProvider[providerId]\n const allowedModelsByProvider = { ...prev.allowedModelsByProvider }\n if (next) {\n const list = current === undefined ? [modelId] : Array.from(new Set([...current, modelId]))\n allowedModelsByProvider[providerId] = list\n } else {\n const baseline = current === undefined ? allModelIds : current\n const list = baseline.filter((id) => id !== modelId)\n allowedModelsByProvider[providerId] = list\n }\n return { ...prev, allowedModelsByProvider }\n })\n }\n\n const resetTenantPicks = (): void => {\n setDirty(true)\n setFeedback(null)\n setEditState({ allowedProviders: null, allowedModelsByProvider: {} })\n }\n\n const handleSave = async (): Promise<void> => {\n setSaving(true)\n setFeedback(null)\n try {\n await runSaveAllowlistMutation({\n operation: async () => {\n const { ok, status, result } = await apiCall<{ error?: string; code?: string }>(\n '/api/ai_assistant/settings/allowlist',\n {\n method: 'PUT',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n allowedProviders: editState.allowedProviders,\n allowedModelsByProvider: editState.allowedModelsByProvider,\n }),\n },\n )\n if (!ok) {\n throw new Error(\n result?.error ?? t('ai_assistant.allowlist.save.error', `Save failed (${status})`),\n )\n }\n },\n context: {},\n })\n const successText = t('ai_assistant.allowlist.save.success', 'Allowlist saved.')\n setFeedback({ kind: 'ok', text: successText })\n flash(successText, 'success')\n setDirty(false)\n await queryClient.invalidateQueries({ queryKey: ['ai_assistant', 'settings'] })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n setFeedback({ kind: 'error', text: message })\n flash(message, 'error')\n } finally {\n setSaving(false)\n }\n }\n\n const handleClear = async (): Promise<void> => {\n setClearing(true)\n setFeedback(null)\n try {\n await runClearAllowlistMutation({\n operation: async () => {\n const { ok, status, result } = await apiCall<{ error?: string; cleared?: boolean }>(\n '/api/ai_assistant/settings/allowlist',\n { method: 'DELETE', credentials: 'include' },\n )\n if (!ok) {\n throw new Error(\n result?.error ?? t('ai_assistant.allowlist.clear.error', `Clear failed (${status})`),\n )\n }\n },\n context: {},\n })\n const successText = t(\n 'ai_assistant.allowlist.clear.success',\n 'Tenant allowlist cleared. Env-only enforcement applies.',\n )\n setFeedback({ kind: 'ok', text: successText })\n flash(successText, 'success')\n setDirty(false)\n await queryClient.invalidateQueries({ queryKey: ['ai_assistant', 'settings'] })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n setFeedback({ kind: 'error', text: message })\n flash(message, 'error')\n } finally {\n setClearing(false)\n }\n }\n\n const envBanner = envAllowedProviders === null && Object.keys(envModelsByProvider).length === 0\n ? null\n : (\n <Alert>\n <AlertTitle>{t('ai_assistant.allowlist.envBanner.title', 'Env allowlist is in effect')}</AlertTitle>\n <AlertDescription className=\"space-y-1\">\n {envAllowedProviders ? (\n <div>\n {t('ai_assistant.allowlist.envBanner.providers', 'OM_AI_AVAILABLE_PROVIDERS')}: <code className=\"font-mono text-xs\">{envAllowedProviders.join(', ')}</code>\n </div>\n ) : null}\n {Object.keys(envModelsByProvider).map((pid) => (\n <div key={pid}>\n <code className=\"font-mono text-xs\">OM_AI_AVAILABLE_MODELS_{pid.toUpperCase()}</code>: {envModelsByProvider[pid].join(', ')}\n </div>\n ))}\n <p className=\"text-xs text-muted-foreground mt-1\">\n {t('ai_assistant.allowlist.envBanner.note', 'Tenant picks may not widen the env list \u2014 values outside it are hidden.')}\n </p>\n </AlertDescription>\n </Alert>\n )\n\n return (\n <div className=\"flex flex-col gap-6 max-w-3xl\">\n {pageHeader}\n\n {envBanner}\n\n {feedback ? (\n <Alert variant={feedback.kind === 'error' ? 'destructive' : undefined}>\n <AlertDescription>{feedback.text}</AlertDescription>\n </Alert>\n ) : null}\n\n <div className=\"rounded-lg border bg-card p-6 space-y-6\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <h2 className=\"text-lg font-semibold\">{t('ai_assistant.allowlist.providers.title', 'Providers')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t(\n 'ai_assistant.allowlist.providers.help',\n 'Untick to forbid the runtime from using a provider for this tenant. Tick all to inherit the env allowlist.',\n )}\n </p>\n </div>\n <span\n className={\n settings.effectiveAllowlist.tenantOverridesActive\n ? 'inline-flex size-8 items-center justify-center rounded-md text-status-success-icon'\n : 'inline-flex size-8 items-center justify-center rounded-md text-status-warning-icon'\n }\n role=\"img\"\n aria-label={\n settings.effectiveAllowlist.tenantOverridesActive\n ? t('ai_assistant.allowlist.badge.active', 'Tenant rules active')\n : t('ai_assistant.allowlist.badge.envOnly', 'Env-only')\n }\n title={\n settings.effectiveAllowlist.tenantOverridesActive\n ? t('ai_assistant.allowlist.badge.active', 'Tenant rules active')\n : t('ai_assistant.allowlist.badge.envOnly', 'Env-only')\n }\n >\n {settings.effectiveAllowlist.tenantOverridesActive ? (\n <ShieldCheck className=\"size-5\" aria-hidden />\n ) : (\n <Shield className=\"size-5\" aria-hidden />\n )}\n </span>\n </div>\n\n {candidateProviders.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {t('ai_assistant.allowlist.providers.empty', 'No configured providers within the env allowlist.')}\n </p>\n ) : (\n <div className=\"space-y-4\">\n {candidateProviders.map((provider) => {\n const enabled = isProviderEnabled(provider.id)\n const envModels = envModelsByProvider[provider.id]\n const candidateModels = envModels\n ? provider.defaultModels.filter((m) => envModels.includes(m.id))\n : provider.defaultModels\n return (\n <div key={provider.id} className=\"rounded-md border p-4 space-y-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <Checkbox\n id={`provider-${provider.id}`}\n checked={enabled}\n onCheckedChange={(value) => toggleProvider(provider.id, value === true)}\n />\n <Label htmlFor={`provider-${provider.id}`} className=\"font-medium\">\n {provider.name}\n </Label>\n {provider.configured ? (\n <Badge variant=\"outline\" className=\"text-xs\">\n {t('ai_assistant.allowlist.providers.configured', 'configured')}\n </Badge>\n ) : (\n <Badge variant=\"outline\" className=\"text-xs text-muted-foreground\">\n {t('ai_assistant.allowlist.providers.notConfigured', 'not configured')}\n </Badge>\n )}\n </div>\n </div>\n\n {enabled && candidateModels.length > 0 ? (\n <div className=\"ml-7 space-y-2\">\n <div className=\"text-xs text-muted-foreground\">\n {t('ai_assistant.allowlist.models.help', 'Tick the models tenants may pick. Empty = no model restriction (inherit env).')}\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-2\">\n {candidateModels.map((model) => {\n const checked = isModelEnabled(provider.id, model.id)\n return (\n <label\n key={`${provider.id}-${model.id}`}\n className=\"flex items-center gap-2 text-sm\"\n >\n <Checkbox\n checked={checked}\n onCheckedChange={(value) => toggleModel(provider.id, model.id, value === true)}\n />\n <span className=\"font-mono text-xs\">{model.id}</span>\n {model.id === provider.defaultModel ? (\n <Badge variant=\"outline\" className=\"text-xs\">\n {t('ai_assistant.allowlist.models.default', 'default')}\n </Badge>\n ) : null}\n </label>\n )\n })}\n </div>\n </div>\n ) : null}\n </div>\n )\n })}\n </div>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button onClick={() => void handleSave()} disabled={!dirty || saving} className=\"gap-2\">\n {saving ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Save className=\"h-4 w-4\" />}\n {t('ai_assistant.allowlist.actions.save', 'Save allowlist')}\n </Button>\n <Button\n variant=\"outline\"\n onClick={resetTenantPicks}\n disabled={saving || clearing}\n className=\"gap-2\"\n >\n {t('ai_assistant.allowlist.actions.reset', 'Reset to env defaults')}\n </Button>\n <Button\n variant=\"ghost\"\n onClick={() => void handleClear()}\n disabled={clearing || saving || !settings.tenantAllowlist}\n className=\"gap-2\"\n >\n {clearing ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Trash2 className=\"h-4 w-4\" />}\n {t('ai_assistant.allowlist.actions.clearStored', 'Clear stored allowlist')}\n </Button>\n </div>\n </div>\n )\n}\n\nexport default AiTenantAllowlistPageClient\n"],
|
|
5
|
-
"mappings": ";AAsGM,SACE,KADF;AApGN,YAAY,WAAW;AACvB,SAAS,UAAU,sBAAsB;AACzC,SAAS,SAAS,MAAM,QAAQ,QAAQ,mBAAmB;AAC3D,SAAS,YAAY;AACrB,SAAS,OAAO,kBAAkB,kBAAkB;AACpD,SAAS,aAAa;AACtB,SAAS,cAAc;AACvB,SAAS,gBAAgB;AACzB,SAAS,aAAa;AACtB,SAAS,SAAS,sBAAsB;AACxC,SAAS,aAAa;AACtB,SAAS,0BAA0B;AAqCnC,eAAe,gBAA2C;AACxD,QAAM,EAAE,QAAQ,OAAO,IAAI,MAAM;AAAA,IAC/B;AAAA,IACA,EAAE,QAAQ,OAAO,aAAa,UAAU;AAAA,IACxC,EAAE,cAAc,6BAA6B;AAAA,EAC/C;AACA,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,4BAA4B,MAAM,GAAG;AAClE,SAAO;AACT;AAQA,SAAS,oBAAoB,UAA6C;AACxE,SAAO;AAAA,IACL,kBAAkB,UAAU,oBAAoB;AAAA,IAChD,yBAAyB,EAAE,GAAI,UAAU,2BAA2B,CAAC,EAAG;AAAA,EAC1E;AACF;AAEO,SAAS,8BAAiD;AAC/D,QAAM,IAAI,KAAK;AACf,QAAM,cAAc,eAAe;AACnC,QAAM,gBAAgB,SAAS,EAAE,UAAU,CAAC,gBAAgB,UAAU,GAAG,SAAS,eAAe,WAAW,EAAE,CAAC;AAE/G,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAoB;AAAA,IAC1D,kBAAkB;AAAA,IAClB,yBAAyB,CAAC;AAAA,EAC5B,CAAC;AACD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAS,KAAK;AAC9C,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAS,KAAK;AAChD,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAS,KAAK;AACpD,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAwD,IAAI;AAClG,QAAM,EAAE,aAAa,yBAAyB,IAAI,mBAAmB;AAAA,IACnE,WAAW;AAAA,EACb,CAAC;AACD,QAAM,EAAE,aAAa,0BAA0B,IAAI,mBAAmB;AAAA,IACpE,WAAW;AAAA,EACb,CAAC;AAED,QAAM,UAAU,MAAM;AACpB,QAAI,cAAc,MAAM;AACtB,mBAAa,oBAAoB,cAAc,KAAK,eAAe,CAAC;AACpE,eAAS,KAAK;AAAA,IAChB;AAAA,EACF,GAAG,CAAC,cAAc,IAAI,CAAC;AAEvB,QAAM,aACJ,qBAAC,SAAI,WAAU,aACb;AAAA,yBAAC,QAAG,WAAU,8CACZ;AAAA,0BAAC,UAAO,WAAU,UAAS;AAAA,MAC1B,EAAE,gCAAgC,+BAA+B;AAAA,OACpE;AAAA,IACA,oBAAC,OAAE,WAAU,yBACV;AAAA,MACC;AAAA,MACA;AAAA,IACF,GACF;AAAA,KACF;AAGF,MAAI,cAAc,WAAW;AAC3B,WACE,qBAAC,SAAI,WAAU,iCACZ;AAAA;AAAA,MACD,qBAAC,SAAI,WAAU,iHAAgH,MAAK,UAClI;AAAA,4BAAC,WAAQ,WAAU,uBAAsB;AAAA,QACxC,EAAE,kCAAkC,yBAAoB;AAAA,SAC3D;AAAA,OACF;AAAA,EAEJ;AAEA,MAAI,cAAc,WAAW,CAAC,cAAc,MAAM;AAChD,WACE,qBAAC,SAAI,WAAU,iCACZ;AAAA;AAAA,MACD,qBAAC,SAAM,
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { Loader2, Save, Shield, Trash2, ShieldCheck } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\n\ntype EnvAllowlistConfig = {\n providers: string[] | null\n modelsByProvider: Record<string, string[]>\n hasRestrictions: boolean\n}\n\ntype TenantAllowlist = {\n allowedProviders: string[] | null\n allowedModelsByProvider: Record<string, string[]>\n}\n\ntype EffectiveAllowlist = {\n providers: string[] | null\n modelsByProvider: Record<string, string[]>\n hasRestrictions: boolean\n tenantOverridesActive: boolean\n}\n\ntype ProviderEntry = {\n id: string\n name: string\n defaultModel: string\n envKey: string | null\n configured: boolean\n defaultModels: Array<{ id: string; name: string; contextWindow?: number; tags?: string[] }>\n}\n\ntype SettingsResponse = {\n availableProviders: ProviderEntry[]\n allowlistProviders?: ProviderEntry[]\n allowlist: EnvAllowlistConfig\n tenantAllowlist: TenantAllowlist | null\n effectiveAllowlist: EffectiveAllowlist\n}\n\nasync function fetchSettings(): Promise<SettingsResponse> {\n const { result, status } = await apiCallOrThrow<SettingsResponse>(\n '/api/ai_assistant/settings',\n { method: 'GET', credentials: 'include' },\n { errorMessage: 'Failed to load AI settings' },\n )\n if (!result) throw new Error(`Failed to load settings (${status})`)\n return result\n}\n\ntype EditState = {\n /** null = \"no tenant restriction (inherit env)\"; array = explicit tenant pick */\n allowedProviders: string[] | null\n allowedModelsByProvider: Record<string, string[]>\n}\n\nfunction snapshotToEditState(snapshot: TenantAllowlist | null): EditState {\n return {\n allowedProviders: snapshot?.allowedProviders ?? null,\n allowedModelsByProvider: { ...(snapshot?.allowedModelsByProvider ?? {}) },\n }\n}\n\nexport function AiTenantAllowlistPageClient(): React.JSX.Element {\n const t = useT()\n const queryClient = useQueryClient()\n const settingsQuery = useQuery({ queryKey: ['ai_assistant', 'settings'], queryFn: fetchSettings, staleTime: 0 })\n\n const [editState, setEditState] = React.useState<EditState>({\n allowedProviders: null,\n allowedModelsByProvider: {},\n })\n const [dirty, setDirty] = React.useState(false)\n const [saving, setSaving] = React.useState(false)\n const [clearing, setClearing] = React.useState(false)\n const [feedback, setFeedback] = React.useState<{ kind: 'ok' | 'error'; text: string } | null>(null)\n const { runMutation: runSaveAllowlistMutation } = useGuardedMutation({\n contextId: 'ai-tenant-allowlist-save',\n })\n const { runMutation: runClearAllowlistMutation } = useGuardedMutation({\n contextId: 'ai-tenant-allowlist-clear',\n })\n\n React.useEffect(() => {\n if (settingsQuery.data) {\n setEditState(snapshotToEditState(settingsQuery.data.tenantAllowlist))\n setDirty(false)\n }\n }, [settingsQuery.data])\n\n const pageHeader = (\n <div className=\"space-y-1\">\n <h1 className=\"flex items-center gap-2 text-2xl font-bold\">\n <Shield className=\"size-6\" />\n {t('ai_assistant.allowlist.title', 'AI provider & model allowlist')}\n </h1>\n <p className=\"text-muted-foreground\">\n {t(\n 'ai_assistant.allowlist.subtitle',\n 'Limit which providers and models the runtime, settings, and chat picker may use for this tenant. The env allowlist is the outer constraint \u2014 tenant picks narrow it further.',\n )}\n </p>\n </div>\n )\n\n if (settingsQuery.isLoading) {\n return (\n <div className=\"flex max-w-3xl flex-col gap-4\">\n {pageHeader}\n <div className=\"flex w-fit items-center gap-2 rounded-md border border-border bg-card px-3 py-2 text-sm text-muted-foreground\" role=\"status\">\n <Loader2 className=\"size-4 animate-spin\" />\n {t('ai_assistant.allowlist.loading', 'Loading allowlist\u2026')}\n </div>\n </div>\n )\n }\n\n if (settingsQuery.isError || !settingsQuery.data) {\n return (\n <div className=\"flex max-w-3xl flex-col gap-4\">\n {pageHeader}\n <Alert status=\"error\">\n <AlertTitle>{t('ai_assistant.allowlist.loadError.title', 'Failed to load allowlist')}</AlertTitle>\n <AlertDescription>\n {settingsQuery.error instanceof Error\n ? settingsQuery.error.message\n : t('ai_assistant.allowlist.loadError.body', 'Try refreshing the page.')}\n </AlertDescription>\n </Alert>\n </div>\n )\n }\n\n const settings = settingsQuery.data\n const envAllowedProviders = settings.allowlist.providers\n const envModelsByProvider = settings.allowlist.modelsByProvider\n\n // Provider universe to render: env-allowed providers (or all configured if env unset).\n const editableProviders = settings.allowlistProviders ?? settings.availableProviders\n const candidateProviders = editableProviders.filter((p) => {\n if (envAllowedProviders === null) return true\n return envAllowedProviders.some((id) => id.toLowerCase() === p.id.toLowerCase())\n })\n\n const tenantPickedProviders = editState.allowedProviders\n const isProviderEnabled = (id: string): boolean => {\n if (tenantPickedProviders === null) return true\n return tenantPickedProviders.includes(id)\n }\n\n const toggleProvider = (id: string, next: boolean): void => {\n setDirty(true)\n setFeedback(null)\n setEditState((prev) => {\n const current = prev.allowedProviders\n if (next) {\n const list = current === null ? [id] : Array.from(new Set([...current, id]))\n return { ...prev, allowedProviders: list }\n }\n const list = current === null\n ? candidateProviders.map((p) => p.id).filter((pid) => pid !== id)\n : current.filter((pid) => pid !== id)\n return { ...prev, allowedProviders: list }\n })\n }\n\n const isModelEnabled = (providerId: string, modelId: string): boolean => {\n const list = editState.allowedModelsByProvider[providerId]\n if (list === undefined) return true\n return list.includes(modelId)\n }\n\n const toggleModel = (providerId: string, modelId: string, next: boolean): void => {\n setDirty(true)\n setFeedback(null)\n const provider = candidateProviders.find((p) => p.id === providerId)\n const allModelIds = provider?.defaultModels.map((m) => m.id) ?? []\n setEditState((prev) => {\n const current = prev.allowedModelsByProvider[providerId]\n const allowedModelsByProvider = { ...prev.allowedModelsByProvider }\n if (next) {\n const list = current === undefined ? [modelId] : Array.from(new Set([...current, modelId]))\n allowedModelsByProvider[providerId] = list\n } else {\n const baseline = current === undefined ? allModelIds : current\n const list = baseline.filter((id) => id !== modelId)\n allowedModelsByProvider[providerId] = list\n }\n return { ...prev, allowedModelsByProvider }\n })\n }\n\n const resetTenantPicks = (): void => {\n setDirty(true)\n setFeedback(null)\n setEditState({ allowedProviders: null, allowedModelsByProvider: {} })\n }\n\n const handleSave = async (): Promise<void> => {\n setSaving(true)\n setFeedback(null)\n try {\n await runSaveAllowlistMutation({\n operation: async () => {\n const { ok, status, result } = await apiCall<{ error?: string; code?: string }>(\n '/api/ai_assistant/settings/allowlist',\n {\n method: 'PUT',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n allowedProviders: editState.allowedProviders,\n allowedModelsByProvider: editState.allowedModelsByProvider,\n }),\n },\n )\n if (!ok) {\n throw new Error(\n result?.error ?? t('ai_assistant.allowlist.save.error', `Save failed (${status})`),\n )\n }\n },\n context: {},\n })\n const successText = t('ai_assistant.allowlist.save.success', 'Allowlist saved.')\n setFeedback({ kind: 'ok', text: successText })\n flash(successText, 'success')\n setDirty(false)\n await queryClient.invalidateQueries({ queryKey: ['ai_assistant', 'settings'] })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n setFeedback({ kind: 'error', text: message })\n flash(message, 'error')\n } finally {\n setSaving(false)\n }\n }\n\n const handleClear = async (): Promise<void> => {\n setClearing(true)\n setFeedback(null)\n try {\n await runClearAllowlistMutation({\n operation: async () => {\n const { ok, status, result } = await apiCall<{ error?: string; cleared?: boolean }>(\n '/api/ai_assistant/settings/allowlist',\n { method: 'DELETE', credentials: 'include' },\n )\n if (!ok) {\n throw new Error(\n result?.error ?? t('ai_assistant.allowlist.clear.error', `Clear failed (${status})`),\n )\n }\n },\n context: {},\n })\n const successText = t(\n 'ai_assistant.allowlist.clear.success',\n 'Tenant allowlist cleared. Env-only enforcement applies.',\n )\n setFeedback({ kind: 'ok', text: successText })\n flash(successText, 'success')\n setDirty(false)\n await queryClient.invalidateQueries({ queryKey: ['ai_assistant', 'settings'] })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n setFeedback({ kind: 'error', text: message })\n flash(message, 'error')\n } finally {\n setClearing(false)\n }\n }\n\n const envBanner = envAllowedProviders === null && Object.keys(envModelsByProvider).length === 0\n ? null\n : (\n <Alert>\n <AlertTitle>{t('ai_assistant.allowlist.envBanner.title', 'Env allowlist is in effect')}</AlertTitle>\n <AlertDescription className=\"space-y-1\">\n {envAllowedProviders ? (\n <div>\n {t('ai_assistant.allowlist.envBanner.providers', 'OM_AI_AVAILABLE_PROVIDERS')}: <code className=\"font-mono text-xs\">{envAllowedProviders.join(', ')}</code>\n </div>\n ) : null}\n {Object.keys(envModelsByProvider).map((pid) => (\n <div key={pid}>\n <code className=\"font-mono text-xs\">OM_AI_AVAILABLE_MODELS_{pid.toUpperCase()}</code>: {envModelsByProvider[pid].join(', ')}\n </div>\n ))}\n <p className=\"text-xs text-muted-foreground mt-1\">\n {t('ai_assistant.allowlist.envBanner.note', 'Tenant picks may not widen the env list \u2014 values outside it are hidden.')}\n </p>\n </AlertDescription>\n </Alert>\n )\n\n return (\n <div className=\"flex flex-col gap-6 max-w-3xl\">\n {pageHeader}\n\n {envBanner}\n\n {feedback ? (\n <Alert status={feedback.kind === 'error' ? 'error' : undefined}>\n <AlertDescription>{feedback.text}</AlertDescription>\n </Alert>\n ) : null}\n\n <div className=\"rounded-lg border bg-card p-6 space-y-6\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <h2 className=\"text-lg font-semibold\">{t('ai_assistant.allowlist.providers.title', 'Providers')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t(\n 'ai_assistant.allowlist.providers.help',\n 'Untick to forbid the runtime from using a provider for this tenant. Tick all to inherit the env allowlist.',\n )}\n </p>\n </div>\n <span\n className={\n settings.effectiveAllowlist.tenantOverridesActive\n ? 'inline-flex size-8 items-center justify-center rounded-md text-status-success-icon'\n : 'inline-flex size-8 items-center justify-center rounded-md text-status-warning-icon'\n }\n role=\"img\"\n aria-label={\n settings.effectiveAllowlist.tenantOverridesActive\n ? t('ai_assistant.allowlist.badge.active', 'Tenant rules active')\n : t('ai_assistant.allowlist.badge.envOnly', 'Env-only')\n }\n title={\n settings.effectiveAllowlist.tenantOverridesActive\n ? t('ai_assistant.allowlist.badge.active', 'Tenant rules active')\n : t('ai_assistant.allowlist.badge.envOnly', 'Env-only')\n }\n >\n {settings.effectiveAllowlist.tenantOverridesActive ? (\n <ShieldCheck className=\"size-5\" aria-hidden />\n ) : (\n <Shield className=\"size-5\" aria-hidden />\n )}\n </span>\n </div>\n\n {candidateProviders.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {t('ai_assistant.allowlist.providers.empty', 'No configured providers within the env allowlist.')}\n </p>\n ) : (\n <div className=\"space-y-4\">\n {candidateProviders.map((provider) => {\n const enabled = isProviderEnabled(provider.id)\n const envModels = envModelsByProvider[provider.id]\n const candidateModels = envModels\n ? provider.defaultModels.filter((m) => envModels.includes(m.id))\n : provider.defaultModels\n return (\n <div key={provider.id} className=\"rounded-md border p-4 space-y-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <Checkbox\n id={`provider-${provider.id}`}\n checked={enabled}\n onCheckedChange={(value) => toggleProvider(provider.id, value === true)}\n />\n <Label htmlFor={`provider-${provider.id}`} className=\"font-medium\">\n {provider.name}\n </Label>\n {provider.configured ? (\n <Badge variant=\"outline\" className=\"text-xs\">\n {t('ai_assistant.allowlist.providers.configured', 'configured')}\n </Badge>\n ) : (\n <Badge variant=\"outline\" className=\"text-xs text-muted-foreground\">\n {t('ai_assistant.allowlist.providers.notConfigured', 'not configured')}\n </Badge>\n )}\n </div>\n </div>\n\n {enabled && candidateModels.length > 0 ? (\n <div className=\"ml-7 space-y-2\">\n <div className=\"text-xs text-muted-foreground\">\n {t('ai_assistant.allowlist.models.help', 'Tick the models tenants may pick. Empty = no model restriction (inherit env).')}\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-2\">\n {candidateModels.map((model) => {\n const checked = isModelEnabled(provider.id, model.id)\n return (\n <label\n key={`${provider.id}-${model.id}`}\n className=\"flex items-center gap-2 text-sm\"\n >\n <Checkbox\n checked={checked}\n onCheckedChange={(value) => toggleModel(provider.id, model.id, value === true)}\n />\n <span className=\"font-mono text-xs\">{model.id}</span>\n {model.id === provider.defaultModel ? (\n <Badge variant=\"outline\" className=\"text-xs\">\n {t('ai_assistant.allowlist.models.default', 'default')}\n </Badge>\n ) : null}\n </label>\n )\n })}\n </div>\n </div>\n ) : null}\n </div>\n )\n })}\n </div>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button onClick={() => void handleSave()} disabled={!dirty || saving} className=\"gap-2\">\n {saving ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Save className=\"h-4 w-4\" />}\n {t('ai_assistant.allowlist.actions.save', 'Save allowlist')}\n </Button>\n <Button\n variant=\"outline\"\n onClick={resetTenantPicks}\n disabled={saving || clearing}\n className=\"gap-2\"\n >\n {t('ai_assistant.allowlist.actions.reset', 'Reset to env defaults')}\n </Button>\n <Button\n variant=\"ghost\"\n onClick={() => void handleClear()}\n disabled={clearing || saving || !settings.tenantAllowlist}\n className=\"gap-2\"\n >\n {clearing ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Trash2 className=\"h-4 w-4\" />}\n {t('ai_assistant.allowlist.actions.clearStored', 'Clear stored allowlist')}\n </Button>\n </div>\n </div>\n )\n}\n\nexport default AiTenantAllowlistPageClient\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["list"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { useQuery } from "@tanstack/react-query";
|
|
5
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
6
|
+
import { DataTable } from "@open-mercato/ui/backend/DataTable";
|
|
7
|
+
import { Button } from "@open-mercato/ui/primitives/button";
|
|
8
|
+
import { Input } from "@open-mercato/ui/primitives/input";
|
|
9
|
+
import { Label } from "@open-mercato/ui/primitives/label";
|
|
10
|
+
import { StatusBadge } from "@open-mercato/ui/primitives/status-badge";
|
|
11
|
+
import { apiCallOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
|
|
12
|
+
const PAGE_SIZE = 50;
|
|
13
|
+
async function fetchModerationFlags(params) {
|
|
14
|
+
const search = new URLSearchParams();
|
|
15
|
+
search.set("page", String(params.page));
|
|
16
|
+
search.set("pageSize", String(PAGE_SIZE));
|
|
17
|
+
if (params.from) search.set("from", params.from);
|
|
18
|
+
if (params.to) search.set("to", params.to);
|
|
19
|
+
if (params.agentId) search.set("agentId", params.agentId);
|
|
20
|
+
if (params.userId) search.set("userId", params.userId);
|
|
21
|
+
const { result, status } = await apiCallOrThrow(
|
|
22
|
+
`/api/ai_assistant/moderation-flags?${search.toString()}`,
|
|
23
|
+
void 0,
|
|
24
|
+
{ errorMessage: "Failed to load moderation flags" }
|
|
25
|
+
);
|
|
26
|
+
if (!result) throw new Error(`Failed to load moderation flags (${status})`);
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
function flaggedCategoryNames(categories) {
|
|
30
|
+
return Object.entries(categories).filter(([, value]) => value.flagged).map(([name]) => name);
|
|
31
|
+
}
|
|
32
|
+
function AiModerationFlagsPageClient() {
|
|
33
|
+
const t = useT();
|
|
34
|
+
const [page, setPage] = React.useState(1);
|
|
35
|
+
const [from, setFrom] = React.useState("");
|
|
36
|
+
const [to, setTo] = React.useState("");
|
|
37
|
+
const [agentId, setAgentId] = React.useState("");
|
|
38
|
+
const [userId, setUserId] = React.useState("");
|
|
39
|
+
const [applied, setApplied] = React.useState({ from: "", to: "", agentId: "", userId: "" });
|
|
40
|
+
const query = useQuery({
|
|
41
|
+
queryKey: ["ai_assistant", "moderation_flags", page, applied],
|
|
42
|
+
queryFn: () => fetchModerationFlags({ page, ...applied }),
|
|
43
|
+
retry: false
|
|
44
|
+
});
|
|
45
|
+
const applyFilters = React.useCallback(() => {
|
|
46
|
+
setPage(1);
|
|
47
|
+
setApplied({ from, to, agentId, userId });
|
|
48
|
+
}, [from, to, agentId, userId]);
|
|
49
|
+
const columns = React.useMemo(
|
|
50
|
+
() => [
|
|
51
|
+
{
|
|
52
|
+
accessorKey: "agentId",
|
|
53
|
+
header: t("ai_assistant.moderationFlags.columns.agent", "Agent")
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
accessorKey: "userId",
|
|
57
|
+
header: t("ai_assistant.moderationFlags.columns.user", "User"),
|
|
58
|
+
meta: { truncate: true, maxWidth: 220 }
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
accessorKey: "categories",
|
|
62
|
+
header: t("ai_assistant.moderationFlags.columns.categories", "Categories"),
|
|
63
|
+
cell: ({ row }) => {
|
|
64
|
+
const names = flaggedCategoryNames(row.original.categories);
|
|
65
|
+
if (names.length === 0) {
|
|
66
|
+
return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2014" });
|
|
67
|
+
}
|
|
68
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: names.map((name) => /* @__PURE__ */ jsx(StatusBadge, { variant: "error", dot: true, children: name }, name)) });
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
accessorKey: "createdAt",
|
|
73
|
+
header: t("ai_assistant.moderationFlags.columns.createdAt", "Flagged at"),
|
|
74
|
+
cell: ({ row }) => new Date(row.original.createdAt).toLocaleString()
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
[t]
|
|
78
|
+
);
|
|
79
|
+
const total = query.data?.total ?? 0;
|
|
80
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
81
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
82
|
+
/* @__PURE__ */ jsx("h2", { className: "text-base font-semibold", children: t("ai_assistant.moderationFlags.title", "Moderation flags") }),
|
|
83
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t(
|
|
84
|
+
"ai_assistant.moderationFlags.subtitle",
|
|
85
|
+
"Inputs blocked by the content safety filter. Category flags and scores only \u2014 no prompt content is stored."
|
|
86
|
+
) })
|
|
87
|
+
] }),
|
|
88
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-end gap-3", children: [
|
|
89
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
90
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "moderation-from", children: t("ai_assistant.moderationFlags.filters.from", "From") }),
|
|
91
|
+
/* @__PURE__ */ jsx(Input, { id: "moderation-from", type: "date", value: from, onChange: (e) => setFrom(e.target.value) })
|
|
92
|
+
] }),
|
|
93
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
94
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "moderation-to", children: t("ai_assistant.moderationFlags.filters.to", "To") }),
|
|
95
|
+
/* @__PURE__ */ jsx(Input, { id: "moderation-to", type: "date", value: to, onChange: (e) => setTo(e.target.value) })
|
|
96
|
+
] }),
|
|
97
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
98
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "moderation-agent", children: t("ai_assistant.moderationFlags.columns.agent", "Agent") }),
|
|
99
|
+
/* @__PURE__ */ jsx(
|
|
100
|
+
Input,
|
|
101
|
+
{
|
|
102
|
+
id: "moderation-agent",
|
|
103
|
+
value: agentId,
|
|
104
|
+
onChange: (e) => setAgentId(e.target.value),
|
|
105
|
+
placeholder: t("ai_assistant.moderationFlags.filters.agentPlaceholder", "module.agent")
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
] }),
|
|
109
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
110
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "moderation-user", children: t("ai_assistant.moderationFlags.columns.user", "User") }),
|
|
111
|
+
/* @__PURE__ */ jsx(Input, { id: "moderation-user", value: userId, onChange: (e) => setUserId(e.target.value) })
|
|
112
|
+
] }),
|
|
113
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: applyFilters, children: t("ai_assistant.moderationFlags.filters.apply", "Apply") })
|
|
114
|
+
] }),
|
|
115
|
+
/* @__PURE__ */ jsx(
|
|
116
|
+
DataTable,
|
|
117
|
+
{
|
|
118
|
+
columns,
|
|
119
|
+
data: query.data?.items ?? [],
|
|
120
|
+
isLoading: query.isLoading,
|
|
121
|
+
error: query.error ? query.error.message : void 0,
|
|
122
|
+
emptyState: t("ai_assistant.moderationFlags.empty", "No flagged messages."),
|
|
123
|
+
pagination: {
|
|
124
|
+
page,
|
|
125
|
+
pageSize: PAGE_SIZE,
|
|
126
|
+
total,
|
|
127
|
+
totalPages: Math.max(1, Math.ceil(total / PAGE_SIZE)),
|
|
128
|
+
onPageChange: setPage
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
] });
|
|
133
|
+
}
|
|
134
|
+
var AiModerationFlagsPageClient_default = AiModerationFlagsPageClient;
|
|
135
|
+
export {
|
|
136
|
+
AiModerationFlagsPageClient,
|
|
137
|
+
AiModerationFlagsPageClient_default as default
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=AiModerationFlagsPageClient.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../src/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/AiModerationFlagsPageClient.tsx"],
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useQuery } from '@tanstack/react-query'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { StatusBadge } from '@open-mercato/ui/primitives/status-badge'\nimport { apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\n\nconst PAGE_SIZE = 50\n\ntype ModerationCategory = { flagged: boolean; score: number }\n\ntype ModerationFlagRow = {\n id: string\n tenantId: string\n organizationId: string | null\n agentId: string\n userId: string\n providerId: string\n modelId: string\n categories: Record<string, ModerationCategory>\n createdAt: string\n}\n\ntype ModerationFlagsResponse = {\n items: ModerationFlagRow[]\n total: number\n page: number\n pageSize: number\n}\n\nasync function fetchModerationFlags(params: {\n page: number\n from: string\n to: string\n agentId: string\n userId: string\n}): Promise<ModerationFlagsResponse> {\n const search = new URLSearchParams()\n search.set('page', String(params.page))\n search.set('pageSize', String(PAGE_SIZE))\n if (params.from) search.set('from', params.from)\n if (params.to) search.set('to', params.to)\n if (params.agentId) search.set('agentId', params.agentId)\n if (params.userId) search.set('userId', params.userId)\n const { result, status } = await apiCallOrThrow<ModerationFlagsResponse>(\n `/api/ai_assistant/moderation-flags?${search.toString()}`,\n undefined,\n { errorMessage: 'Failed to load moderation flags' },\n )\n if (!result) throw new Error(`Failed to load moderation flags (${status})`)\n return result\n}\n\nfunction flaggedCategoryNames(categories: Record<string, ModerationCategory>): string[] {\n return Object.entries(categories)\n .filter(([, value]) => value.flagged)\n .map(([name]) => name)\n}\n\nexport function AiModerationFlagsPageClient() {\n const t = useT()\n const [page, setPage] = React.useState(1)\n const [from, setFrom] = React.useState('')\n const [to, setTo] = React.useState('')\n const [agentId, setAgentId] = React.useState('')\n const [userId, setUserId] = React.useState('')\n const [applied, setApplied] = React.useState({ from: '', to: '', agentId: '', userId: '' })\n\n const query = useQuery<ModerationFlagsResponse>({\n queryKey: ['ai_assistant', 'moderation_flags', page, applied],\n queryFn: () => fetchModerationFlags({ page, ...applied }),\n retry: false,\n })\n\n const applyFilters = React.useCallback(() => {\n setPage(1)\n setApplied({ from, to, agentId, userId })\n }, [from, to, agentId, userId])\n\n const columns = React.useMemo<ColumnDef<ModerationFlagRow, unknown>[]>(\n () => [\n {\n accessorKey: 'agentId',\n header: t('ai_assistant.moderationFlags.columns.agent', 'Agent'),\n },\n {\n accessorKey: 'userId',\n header: t('ai_assistant.moderationFlags.columns.user', 'User'),\n meta: { truncate: true, maxWidth: 220 },\n },\n {\n accessorKey: 'categories',\n header: t('ai_assistant.moderationFlags.columns.categories', 'Categories'),\n cell: ({ row }) => {\n const names = flaggedCategoryNames(row.original.categories)\n if (names.length === 0) {\n return <span className=\"text-muted-foreground\">\u2014</span>\n }\n return (\n <div className=\"flex flex-wrap gap-1\">\n {names.map((name) => (\n <StatusBadge key={name} variant=\"error\" dot>\n {name}\n </StatusBadge>\n ))}\n </div>\n )\n },\n },\n {\n accessorKey: 'createdAt',\n header: t('ai_assistant.moderationFlags.columns.createdAt', 'Flagged at'),\n cell: ({ row }) => new Date(row.original.createdAt).toLocaleString(),\n },\n ],\n [t],\n )\n\n const total = query.data?.total ?? 0\n\n return (\n <div className=\"flex flex-col gap-4\">\n <div>\n <h2 className=\"text-base font-semibold\">\n {t('ai_assistant.moderationFlags.title', 'Moderation flags')}\n </h2>\n <p className=\"text-xs text-muted-foreground\">\n {t(\n 'ai_assistant.moderationFlags.subtitle',\n 'Inputs blocked by the content safety filter. Category flags and scores only \u2014 no prompt content is stored.',\n )}\n </p>\n </div>\n\n <div className=\"flex flex-wrap items-end gap-3\">\n <div className=\"flex flex-col gap-1\">\n <Label htmlFor=\"moderation-from\">{t('ai_assistant.moderationFlags.filters.from', 'From')}</Label>\n <Input id=\"moderation-from\" type=\"date\" value={from} onChange={(e) => setFrom(e.target.value)} />\n </div>\n <div className=\"flex flex-col gap-1\">\n <Label htmlFor=\"moderation-to\">{t('ai_assistant.moderationFlags.filters.to', 'To')}</Label>\n <Input id=\"moderation-to\" type=\"date\" value={to} onChange={(e) => setTo(e.target.value)} />\n </div>\n <div className=\"flex flex-col gap-1\">\n <Label htmlFor=\"moderation-agent\">{t('ai_assistant.moderationFlags.columns.agent', 'Agent')}</Label>\n <Input\n id=\"moderation-agent\"\n value={agentId}\n onChange={(e) => setAgentId(e.target.value)}\n placeholder={t('ai_assistant.moderationFlags.filters.agentPlaceholder', 'module.agent')}\n />\n </div>\n <div className=\"flex flex-col gap-1\">\n <Label htmlFor=\"moderation-user\">{t('ai_assistant.moderationFlags.columns.user', 'User')}</Label>\n <Input id=\"moderation-user\" value={userId} onChange={(e) => setUserId(e.target.value)} />\n </div>\n <Button type=\"button\" variant=\"outline\" onClick={applyFilters}>\n {t('ai_assistant.moderationFlags.filters.apply', 'Apply')}\n </Button>\n </div>\n\n <DataTable<ModerationFlagRow>\n columns={columns}\n data={query.data?.items ?? []}\n isLoading={query.isLoading}\n error={query.error ? (query.error as Error).message : undefined}\n emptyState={t('ai_assistant.moderationFlags.empty', 'No flagged messages.')}\n pagination={{\n page,\n pageSize: PAGE_SIZE,\n total,\n totalPages: Math.max(1, Math.ceil(total / PAGE_SIZE)),\n onPageChange: setPage,\n }}\n />\n </div>\n )\n}\n\nexport default AiModerationFlagsPageClient\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Page, PageBody } from "@open-mercato/ui/backend/Page";
|
|
3
|
+
import { AiModerationFlagsPageClient } from "./AiModerationFlagsPageClient.js";
|
|
4
|
+
async function AiModerationFlagsPage() {
|
|
5
|
+
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(AiModerationFlagsPageClient, {}) }) });
|
|
6
|
+
}
|
|
7
|
+
export {
|
|
8
|
+
AiModerationFlagsPage as default
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=page.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../src/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/page.tsx"],
|
|
4
|
+
"sourcesContent": ["import { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { AiModerationFlagsPageClient } from './AiModerationFlagsPageClient'\n\nexport default async function AiModerationFlagsPage() {\n return (\n <Page>\n <PageBody>\n <AiModerationFlagsPageClient />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "AAOQ;AAPR,SAAS,MAAM,gBAAgB;AAC/B,SAAS,mCAAmC;AAE5C,eAAO,wBAA+C;AACpD,SACE,oBAAC,QACC,8BAAC,YACC,8BAAC,+BAA4B,GAC/B,GACF;AAEJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
const moderationIcon = React.createElement(
|
|
3
|
+
"svg",
|
|
4
|
+
{ width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" },
|
|
5
|
+
React.createElement("path", { d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" }),
|
|
6
|
+
React.createElement("path", { d: "M12 8v4" }),
|
|
7
|
+
React.createElement("path", { d: "M12 16h.01" })
|
|
8
|
+
);
|
|
9
|
+
const metadata = {
|
|
10
|
+
requireAuth: true,
|
|
11
|
+
requireFeatures: ["ai_assistant.settings.manage"],
|
|
12
|
+
pageTitle: "Moderation flags",
|
|
13
|
+
pageTitleKey: "ai_assistant.moderationFlags.navTitle",
|
|
14
|
+
pageGroup: "Module Configs",
|
|
15
|
+
pageGroupKey: "settings.sections.moduleConfigs",
|
|
16
|
+
pageOrder: 432,
|
|
17
|
+
icon: moderationIcon,
|
|
18
|
+
pageContext: "settings",
|
|
19
|
+
breadcrumb: [
|
|
20
|
+
{ label: "Moderation flags", labelKey: "ai_assistant.moderationFlags.navTitle" }
|
|
21
|
+
]
|
|
22
|
+
};
|
|
23
|
+
export {
|
|
24
|
+
metadata
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=page.meta.js.map
|
package/dist/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/page.meta.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../src/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/page.meta.ts"],
|
|
4
|
+
"sourcesContent": ["import React from 'react'\n\nconst moderationIcon = React.createElement(\n 'svg',\n { width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' },\n React.createElement('path', { d: 'M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z' }),\n React.createElement('path', { d: 'M12 8v4' }),\n React.createElement('path', { d: 'M12 16h.01' }),\n)\n\nexport const metadata = {\n requireAuth: true,\n requireFeatures: ['ai_assistant.settings.manage'],\n pageTitle: 'Moderation flags',\n pageTitleKey: 'ai_assistant.moderationFlags.navTitle',\n pageGroup: 'Module Configs',\n pageGroupKey: 'settings.sections.moduleConfigs',\n pageOrder: 432,\n icon: moderationIcon,\n pageContext: 'settings' as const,\n breadcrumb: [\n { label: 'Moderation flags', labelKey: 'ai_assistant.moderationFlags.navTitle' },\n ],\n} as const\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,WAAW;AAElB,MAAM,iBAAiB,MAAM;AAAA,EAC3B;AAAA,EACA,EAAE,OAAO,IAAI,QAAQ,IAAI,SAAS,aAAa,MAAM,QAAQ,QAAQ,gBAAgB,aAAa,GAAG,eAAe,SAAS,gBAAgB,QAAQ;AAAA,EACrJ,MAAM,cAAc,QAAQ,EAAE,GAAG,8CAA8C,CAAC;AAAA,EAChF,MAAM,cAAc,QAAQ,EAAE,GAAG,UAAU,CAAC;AAAA,EAC5C,MAAM,cAAc,QAAQ,EAAE,GAAG,aAAa,CAAC;AACjD;AAEO,MAAM,WAAW;AAAA,EACtB,aAAa;AAAA,EACb,iBAAiB,CAAC,8BAA8B;AAAA,EAChD,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,MAAM;AAAA,EACN,aAAa;AAAA,EACb,YAAY;AAAA,IACV,EAAE,OAAO,oBAAoB,UAAU,wCAAwC;AAAA,EACjF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js
CHANGED
|
@@ -203,7 +203,7 @@ function ChatLane({ agent, debug }) {
|
|
|
203
203
|
if (seeds.length > 0) setUiParts(seeds);
|
|
204
204
|
}, []);
|
|
205
205
|
if (agent.executionMode !== "chat") {
|
|
206
|
-
return /* @__PURE__ */ jsxs(Alert, {
|
|
206
|
+
return /* @__PURE__ */ jsxs(Alert, { status: "information", "data-ai-playground-unsupported": "chat", children: [
|
|
207
207
|
/* @__PURE__ */ jsx(AlertTitle, { children: t(
|
|
208
208
|
"ai_assistant.playground.chat.notSupportedTitle",
|
|
209
209
|
"Chat mode is not available for this agent."
|
|
@@ -285,7 +285,7 @@ function ObjectLane({ agent }) {
|
|
|
285
285
|
}
|
|
286
286
|
});
|
|
287
287
|
if (!isSupported) {
|
|
288
|
-
return /* @__PURE__ */ jsxs(Alert, {
|
|
288
|
+
return /* @__PURE__ */ jsxs(Alert, { status: "information", "data-ai-playground-unsupported": "object", children: [
|
|
289
289
|
/* @__PURE__ */ jsx(AlertTitle, { children: t(
|
|
290
290
|
"ai_assistant.playground.object.notSupportedTitle",
|
|
291
291
|
"Object mode is not available for this agent."
|
|
@@ -336,7 +336,7 @@ function ObjectLane({ agent }) {
|
|
|
336
336
|
)
|
|
337
337
|
] })
|
|
338
338
|
] }),
|
|
339
|
-
error ? /* @__PURE__ */ jsxs(Alert, {
|
|
339
|
+
error ? /* @__PURE__ */ jsxs(Alert, { status: "error", "data-ai-playground-object-error": error.code ?? "unknown", children: [
|
|
340
340
|
/* @__PURE__ */ jsx(AlertTitle, { children: t("ai_assistant.playground.object.errorTitle", "Object run failed") }),
|
|
341
341
|
/* @__PURE__ */ jsxs(AlertDescription, { children: [
|
|
342
342
|
error.code ? /* @__PURE__ */ jsx("span", { className: "mr-2 font-mono text-xs", children: error.code }) : null,
|
|
@@ -413,7 +413,7 @@ function AiPlaygroundPageClient() {
|
|
|
413
413
|
);
|
|
414
414
|
}
|
|
415
415
|
if (isError) {
|
|
416
|
-
return /* @__PURE__ */ jsxs(Alert, {
|
|
416
|
+
return /* @__PURE__ */ jsxs(Alert, { status: "error", "data-ai-playground-error": true, children: [
|
|
417
417
|
/* @__PURE__ */ jsx(AlertTitle, { children: t("ai_assistant.playground.loadErrorTitle", "Failed to load AI agents") }),
|
|
418
418
|
/* @__PURE__ */ jsxs(AlertDescription, { children: [
|
|
419
419
|
/* @__PURE__ */ jsx("span", { children: error instanceof Error ? error.message : String(error) }),
|