fix: hide unstable Agnes 18s video duration
This commit is contained in:
@@ -186,7 +186,7 @@ Primary SQL tables touched directly in API routes include:
|
||||
|
||||
Yuanjie Manifest references use `$inputImages.urls` for provider-facing JSON fields. For image-to-image, `/api/generate/image` reads the primary `image` plus `extraImages` and sends all references to `src/lib/user-api-manifest-executor.ts`; for image-to-video, `/api/generate/video` reads `image`, `images`, and `extraImages` before Manifest execution. The executor uploads data URL references into storage before rendering Yuanjie `params.images`, top-level `images`, `reference_urls`, or `base64Array`. `referenceImageAnnotations` is an API payload field rather than a Manifest variable; image/video routes use `src/lib/reference-image-prompt.ts` to merge `@参考图N` token mappings into the upstream prompt so existing Manifest templates receive the mapping through `$prompt`. Yuanjie video templates keep documented model-specific fields inside `src/lib/yuanjie-video-model-templates.ts`, including first/last reference fields and mode fields such as `input_reference`, `reference_urls`, `img_url`, `image_tail`, `ratio`, `size`, and `generation_mode`.
|
||||
|
||||
`src/lib/agnes-model-templates.ts` is the canonical source for Agnes AI built-in free templates. Agnes Video V2.0 uses Manifest `POST /v1/videos` plus `/agnesapi` polling, but duration must be sent as `num_frames` rather than `duration`. `/api/generate/video` maps Agnes UI durations 3/5/10/18 seconds to 24fps frame counts 81/121/241/441 and sends `frame_rate: 24`; in image-to-video mode the top-level `image` is the provider's starting/first frame field, not a generic non-first-frame reference slot. The Manifest executor keeps Agnes-style total polling budgets separate from per-request submit/poll timeouts, so one slow or transiently failed poll request does not end the whole async video job before the full video budget expires.
|
||||
`src/lib/agnes-model-templates.ts` is the canonical source for Agnes AI built-in free templates. Agnes Video V2.0 uses Manifest `POST /v1/videos` plus `/agnesapi` polling, but duration must be sent as `num_frames` rather than `duration`. `/api/generate/video` maps the currently stable Agnes UI durations 3/5/10 seconds to 24fps frame counts 81/121/241 and sends `frame_rate: 24`; 18 seconds is intentionally hidden and rejected because production evidence showed the upstream task moves past creation but returns `failed` for that length. In image-to-video mode the top-level `image` is the provider's starting/first frame field, not a generic non-first-frame reference slot. The Manifest executor keeps Agnes-style total polling budgets separate from per-request submit/poll timeouts, so one slow or transiently failed poll request does not end the whole async video job before the full video budget expires.
|
||||
|
||||
`src/lib/yuanjie-system-manifest.ts` provides the runtime bridge for existing admin system API rows that were created before Manifest-backed Yuanjie templates. It exposes built-in capabilities to `/api/model-config` even when `manifest_path` is empty, and when a known 元界 system API is resolved directly or as a default-model polling candidate it writes missing or stale `system-api-manifests/<systemApiId>.json`, normalizes `api_url` back to the 元界 base URL, and preserves the encrypted API key and administrator pricing.
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ Use this guide when the user reports behavior. Start from the symptom row, inspe
|
||||
| 元界 AI 同步后出现大量接口/参数名模型或模型行反复显示 Key | `src/app/api/admin/system-apis/yuanjie-capabilities/route.ts`, `src/lib/yuanjie-image-model-templates.ts`, `src/lib/yuanjie-video-model-templates.ts`, `src/lib/yuanjie-template-installer.ts`, `src/components/admin/api-management-tab.tsx` | 元界不应再从 `/v1/skills` 或 `/v1/skills/guide` 猜模型,也不应在 `智能配置 API` 页面暴露内置模板安装/同步入口。检查安装路由是否使用内置图片/视频模板、是否只删除当前媒体类型的 `provider = '元界 AI'` 行、是否创建 inactive rows and per-model Manifest files, and whether admins configure Key/pricing/usage modes/enablement per model through the system-default-model management flow. The admin list should not show repeated imported key placeholders, and the create page should show only documented controls from the selected template capabilities. |
|
||||
| 元界任务在元界后台成功但妙境报模型繁忙或接口路径不存在 | `src/lib/yuanjie-image-model-templates.ts`, `src/lib/yuanjie-video-model-templates.ts`, `src/lib/user-api-manifest-executor.ts`, `src/app/api/generate/image/route.ts`, `src/app/api/generate/video/route.ts` | Check whether the Manifest poll endpoint uses `path: "v1/media/status"` plus `query: { task_id: "{task_id}" }`. If the path is stored as `v1/media/status?task_id={task_id}`, the executor can encode the query string into the pathname and 元界 will return a not-found error even though the create request already produced a task. Also verify 元界 media templates use `finalPath: "is_final"`, `finalValues: [true]`, `statusPath: "state"`, `successValues: ["success"]`, and `failureValues: ["failed"]`; `status` / `status_group` are display fields only. |
|
||||
| 元界后台显示已生成图片但妙境任务失败,日志出现下载 403、timeout 或保存失败 | `src/app/api/generate/image/route.ts`, `src/lib/media-storage.ts`, `src/lib/remote-fetch.ts`, `src/lib/user-api-manifest-executor.ts` | 这通常不是元界提交或轮询失败,而是 Manifest 结果 URL 返回后,妙境下载外部图片或保存原图/缩略图失败。先查 PM2 日志中 `[User API Manifest Image] Failed to persist generated image`,区分 `下载图片失败: 403`、`fetch failed`、`Persist generated image media timed out`、对象存储/缩略图错误。外部生成图 URL 应通过 `fetchPublicHttpUrlWithRetry` 发送浏览器式 `User-Agent`/`Accept` 并有限重试;`/api/generate/image` 应返回“上游已返回生成结果,但平台下载或保存结果图片失败”,不要再误包装为“上游返回图片分辨率不符合”或泛化成模型繁忙。 |
|
||||
| Agnes 视频任务先显示 `in_progress` 后失败,错误为裸 `fetch failed` 或历史不写入 | `src/lib/agnes-model-templates.ts`, `src/lib/user-api-manifest-executor.ts`, `src/app/api/generate/video/route.ts`, `src/lib/generation-job-worker.ts` | 先区分提交、轮询、结果视频下载保存、历史写入四段。Agnes V2.0 使用 `POST /v1/videos` 和 `GET /agnesapi?video_id=...&model_name=agnes-video-v2.0`,`remixed_from_video_id` 在官方完成响应中是视频 URL。Manifest 执行器会把网络异常包装成“上游任务创建/轮询网络连接失败”;视频结果保存失败应显示“上游已返回视频地址,但平台下载或保存结果视频失败”。若 job 成功但 `works` 没有记录,查 `[generation-worker] creation history persistence failed` 中的内部 URL。Agnes 时长不要传 `duration`,3/5/10/18 秒映射为 `num_frames` 81/121/241/441,`frame_rate=24`;Agnes 视频是后台异步任务,`/api/generate/video` 给它单独 20 分钟轮询窗口,刷新/切页由 generation job 恢复链路继续显示状态。Manifest 总轮询预算要和单次请求超时分开,单次轮询 502/503/504、`fetch failed` 或网络超时应先更新任务进度并继续轮询,只有总预算耗尽才标记超时失败。 |
|
||||
| Agnes 视频任务先显示 `in_progress` 后失败,错误为裸 `fetch failed` 或历史不写入 | `src/lib/agnes-model-templates.ts`, `src/lib/user-api-manifest-executor.ts`, `src/app/api/generate/video/route.ts`, `src/lib/generation-job-worker.ts` | 先区分提交、轮询、结果视频下载保存、历史写入四段。Agnes V2.0 使用 `POST /v1/videos` 和 `GET /agnesapi?video_id=...&model_name=agnes-video-v2.0`,`remixed_from_video_id` 在官方完成响应中是视频 URL。Manifest 执行器会把网络异常包装成“上游任务创建/轮询网络连接失败”;视频结果保存失败应显示“上游已返回视频地址,但平台下载或保存结果视频失败”。若 job 成功但 `works` 没有记录,查 `[generation-worker] creation history persistence failed` 中的内部 URL。Agnes 时长不要传 `duration`,当前仅开放稳定的 3/5/10 秒并映射为 `num_frames` 81/121/241,`frame_rate=24`;18 秒在生产中会进入轮询后返回上游 `failed`,应从能力列表隐藏并在后端拒绝旧请求。Agnes 视频是后台异步任务,`/api/generate/video` 给它单独 20 分钟轮询窗口,刷新/切页由 generation job 恢复链路继续显示状态。Manifest 总轮询预算要和单次请求超时分开,单次轮询 502/503/504、`fetch failed` 或网络超时应先更新任务进度并继续轮询,只有总预算耗尽才标记超时失败。 |
|
||||
| 元界图生图提交后妙境报 `Manifest 未能从 ... 读取任务 ID` or generic `模型繁忙` while 元界 may have accepted the job | `src/lib/server-api-config.ts`, `src/lib/user-api-manifest-executor.ts`, `src/lib/yuanjie-image-model-templates.ts`, `src/lib/yuanjie-video-model-templates.ts` | The submit response can put the task identifier inside nested `result` objects. The executor must normalize `task_id`, `taskId`, `id`, and nested `data/result/output` objects before polling. Template `taskIdPath` should include `result.task_id`, `result.taskId`, and `result.id` before the broad `result` fallback. For system default polling, `resolveSystemApiPollingCandidates(...)` must also run `ensureYuanjieSystemApiManifest(...)`; otherwise stale production `system-api-manifests/<id>.json` files can keep old `$inputImages.dataUrls` and old task-id paths even when source templates are fixed. |
|
||||
| 视频系统模型出现在错误入口或缺少参数选项 | `src/lib/server-api-config.ts`, `src/components/admin/api-management-tab.tsx`, `src/components/create/text-to-video.tsx`, `src/components/create/image-to-video.tsx`, `src/lib/model-capabilities.ts` | Check `system_api_configs.video_usage_modes`. 文生视频 should only show rows including `text-to-video`; 图生视频 should only show rows including `image-to-video`. Selected system video models should read Manifest `capabilities` for aspect ratio, duration, and resolution controls. |
|
||||
| 管理后台刷新后跳回仪表盘 | `src/modules/console/pages/console-dashboard-page.tsx` | The active view should be restored from `sessionStorage` on refresh and removed on logout. If it jumps to dashboard after a plain refresh, inspect the session key `miaojing_console_active_view` and whether the view is still allowed by the current membership/admin config. |
|
||||
|
||||
@@ -35,7 +35,7 @@ Use this document before changing non-generic provider/platform behavior. If a u
|
||||
- Agnes built-in templates belong to the `系统默认模型` management flow. Do not expose them as a generic `智能配置 API` import; keep one system API row per model and one independent `system-api-manifests/<systemApiId>.json` file for each image/video row.
|
||||
- The API base is `https://apihub.agnes-ai.com`. Image models `agnes-image-2.1-flash` and `agnes-image-2.0-flash` use `POST /v1/images/generations` with `model`, `prompt`, `size`, and optional top-level `image: string[]` for image-to-image. URL output must be requested as `extra_body.response_format = "url"`; do not put `response_format` at the top level. Read `data.*.url`, with `data.*.b64_json` as a fallback.
|
||||
- Video model `agnes-video-v2.0` uses `POST /v1/videos` to create an async task and `GET /agnesapi?video_id={video_id}&model_name=agnes-video-v2.0` to poll. Treat `video_id`, `task_id`, or `id` as the task identifier, `completed` as success, `failed` as failure, and read the final video from `remixed_from_video_id`, `video_url`, or `url`.
|
||||
- Agnes Video duration is controlled by `num_frames`, not a `duration` request field. The create route maps UI durations 3/5/10/18 seconds to the documented 24fps frame counts: 81/121/241/441, and sends `frame_rate: 24`.
|
||||
- Agnes Video duration is controlled by `num_frames`, not a `duration` request field. The create route currently exposes only production-stable UI durations 3/5/10 seconds, maps them to the documented 24fps frame counts 81/121/241, and sends `frame_rate: 24`. Do not re-enable 18 seconds from stale Manifests or older docs until production evidence shows Agnes no longer returns upstream `failed` for that length; backend requests for 18 seconds should be rejected quickly with a clear user-facing message.
|
||||
- Agnes Video generation can spend minutes in the async task. Keep the Manifest total polling budget separate from per-request submit/poll timeouts, and treat single poll-side 502/503/504, `fetch failed`, or network timeouts as transient until the total budget expires.
|
||||
- For image-to-video, Agnes uses the top-level `image` field as the starting/first frame. Do not treat Agnes Video as a generic multi-reference video model unless Agnes adds a separate reference-image field.
|
||||
- Text/multimodal models `agnes-2.0-flash` and `agnes-1.5-flash` use OpenAI-compatible `POST /v1/chat/completions`; they do not need Manifest files and can be used by prompt optimization or reverse prompt through the existing system text API path.
|
||||
|
||||
@@ -14,7 +14,9 @@ const {
|
||||
AGNES_VIDEO_MODEL_TEMPLATES,
|
||||
AGNES_TEXT_MODEL_TEMPLATES,
|
||||
AGNES_VIDEO_FRAME_RATE,
|
||||
normalizeAgnesVideoDuration,
|
||||
getAgnesVideoNumFrames,
|
||||
getAgnesModelCapabilities,
|
||||
buildAgnesImageManifestBundle,
|
||||
buildAgnesVideoManifestBundle,
|
||||
buildAgnesCapabilitiesText,
|
||||
@@ -110,17 +112,20 @@ await runTest('Agnes video Manifest creates async task and polls by video_id', (
|
||||
|
||||
await runTest('Agnes video duration options map to documented frame counts at 24fps', () => {
|
||||
assert.equal(AGNES_VIDEO_FRAME_RATE, 24);
|
||||
assert.deepEqual(AGNES_VIDEO_MODEL_TEMPLATES[0].capabilities.durations?.map(item => item.value), ['3', '5', '10', '18']);
|
||||
assert.deepEqual(AGNES_VIDEO_MODEL_TEMPLATES[0].capabilities.durations?.map(item => item.value), ['3', '5', '10']);
|
||||
assert.equal(normalizeAgnesVideoDuration(18), null);
|
||||
assert.equal(getAgnesVideoNumFrames(3), 81);
|
||||
assert.equal(getAgnesVideoNumFrames(5), 121);
|
||||
assert.equal(getAgnesVideoNumFrames(10), 241);
|
||||
assert.equal(getAgnesVideoNumFrames(18), 441);
|
||||
assert.deepEqual(getAgnesModelCapabilities('agnes-video-v2.0')?.durations?.map(item => item.value), ['3', '5', '10']);
|
||||
|
||||
const videoRoute = read('src/app/api/generate/video/route.ts');
|
||||
assert.match(videoRoute, /normalizeAgnesVideoDuration\(duration\)/);
|
||||
assert.match(videoRoute, /Agnes Video V2\.0 当前仅开放 3、5、10 秒/);
|
||||
assert.match(videoRoute, /const useAgnesVideoParams = isAgnesVideoApi\(resolvedCustomApiConfig\)/);
|
||||
assert.match(videoRoute, /getAgnesVideoNumFrames\(duration\)/);
|
||||
assert.match(videoRoute, /getAgnesVideoNumFrames\(resolvedAgnesDuration\)/);
|
||||
assert.match(videoRoute, /fps:\s*useAgnesVideoParams\s*\?\s*AGNES_VIDEO_FRAME_RATE\s*:\s*fps/);
|
||||
assert.match(videoRoute, /num_frames:\s*useAgnesVideoParams\s*\?\s*getAgnesVideoNumFrames\(duration\)\s*:\s*undefined/);
|
||||
assert.match(videoRoute, /num_frames:\s*useAgnesVideoParams\s*\?\s*getAgnesVideoNumFrames\(resolvedAgnesDuration\)\s*:\s*undefined/);
|
||||
assert.match(videoRoute, /timeoutMs:\s*useAgnesVideoParams\s*\?\s*AGNES_VIDEO_GENERATION_TIMEOUT\s*:\s*GENERATION_TIMEOUT/);
|
||||
});
|
||||
|
||||
@@ -182,6 +187,14 @@ await runTest('Agnes installer source creates free inactive rows with empty API
|
||||
assert.match(installer, /Agnes 免费模型/);
|
||||
});
|
||||
|
||||
await runTest('Agnes system model capabilities use built-in fallback so stale manifests do not expose unstable 18s', () => {
|
||||
const serverConfig = read('src/lib/server-api-config.ts');
|
||||
|
||||
assert.match(serverConfig, /getAgnesModelCapabilities/);
|
||||
assert.match(serverConfig, /getAgnesSystemApiCapabilitiesFallback/);
|
||||
assert.match(serverConfig, /getAgnesSystemApiCapabilitiesFallback\(row\)\s*\|\|\s*readManifestCapabilities/);
|
||||
});
|
||||
|
||||
await runTest('admin UI and docs expose Agnes as system-default built-in templates, not smart import', () => {
|
||||
const adminTab = read('src/components/admin/api-management-tab.tsx');
|
||||
const apiReference = read('docs/codex-miaojing/api-reference.md');
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
import { executeUserApiManifest } from '@/lib/user-api-manifest-executor';
|
||||
import { buildReferenceImagePrompt } from '@/lib/reference-image-prompt';
|
||||
import { fetchPublicHttpUrlWithRetry } from '@/lib/remote-fetch';
|
||||
import { AGNES_PROVIDER_NAME, AGNES_VIDEO_FRAME_RATE, getAgnesVideoNumFrames } from '@/lib/agnes-model-templates';
|
||||
import { AGNES_PROVIDER_NAME, AGNES_VIDEO_FRAME_RATE, getAgnesVideoNumFrames, normalizeAgnesVideoDuration } from '@/lib/agnes-model-templates';
|
||||
|
||||
interface CustomApiConfig {
|
||||
apiUrl: string;
|
||||
@@ -559,6 +559,14 @@ export async function POST(request: NextRequest) {
|
||||
try {
|
||||
if (resolvedCustomApiConfig.manifestPath) {
|
||||
const useAgnesVideoParams = isAgnesVideoApi(resolvedCustomApiConfig);
|
||||
const agnesVideoDuration = useAgnesVideoParams ? normalizeAgnesVideoDuration(duration) : null;
|
||||
if (useAgnesVideoParams && agnesVideoDuration === null) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Agnes Video V2.0 当前仅开放 3、5、10 秒,18 秒上游生成不稳定,请改选 10 秒后重试' },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
const resolvedAgnesDuration = agnesVideoDuration ?? undefined;
|
||||
const manifestResult = await executeUserApiManifest({
|
||||
manifestPath: resolvedCustomApiConfig.manifestPath,
|
||||
apiUrl: resolvedCustomApiConfig.apiUrl,
|
||||
@@ -568,12 +576,12 @@ export async function POST(request: NextRequest) {
|
||||
params: {
|
||||
n: 1,
|
||||
aspect_ratio: aspectRatio,
|
||||
duration,
|
||||
duration: useAgnesVideoParams ? resolvedAgnesDuration : duration,
|
||||
resolution,
|
||||
quality: quality || mode,
|
||||
mode: mode || quality,
|
||||
fps: useAgnesVideoParams ? AGNES_VIDEO_FRAME_RATE : fps,
|
||||
num_frames: useAgnesVideoParams ? getAgnesVideoNumFrames(duration) : undefined,
|
||||
num_frames: useAgnesVideoParams ? getAgnesVideoNumFrames(resolvedAgnesDuration) : undefined,
|
||||
negative_prompt: negativePrompt,
|
||||
},
|
||||
inputImages: referenceImages,
|
||||
|
||||
@@ -34,23 +34,35 @@ const option = (value: string, label = value) => ({ value, label });
|
||||
const options = (values: string[]) => values.map(value => option(value));
|
||||
|
||||
export const AGNES_VIDEO_FRAME_RATE = 24;
|
||||
const AGNES_VIDEO_MIN_DURATION = 3;
|
||||
const AGNES_VIDEO_MAX_DURATION = 18;
|
||||
const AGNES_VIDEO_MAX_DURATION = 10;
|
||||
const AGNES_STABLE_VIDEO_DURATIONS = ['3', '5', '10'];
|
||||
|
||||
export function normalizeAgnesVideoDuration(duration: number | string | undefined): number | null {
|
||||
const parsed = Number(duration);
|
||||
if (!Number.isFinite(parsed)) return 5;
|
||||
const seconds = Math.round(parsed);
|
||||
return AGNES_STABLE_VIDEO_DURATIONS.includes(String(seconds)) ? seconds : null;
|
||||
}
|
||||
|
||||
export function getAgnesVideoNumFrames(duration: number | string | undefined): number {
|
||||
const parsed = Number(duration);
|
||||
const seconds = Number.isFinite(parsed)
|
||||
? Math.min(Math.max(Math.round(parsed), AGNES_VIDEO_MIN_DURATION), AGNES_VIDEO_MAX_DURATION)
|
||||
: 5;
|
||||
const normalizedDuration = normalizeAgnesVideoDuration(duration);
|
||||
const seconds = normalizedDuration === null ? AGNES_VIDEO_MAX_DURATION : normalizedDuration;
|
||||
const documentedFrameCounts: Record<number, number> = {
|
||||
3: 81,
|
||||
5: 121,
|
||||
10: 241,
|
||||
18: 441,
|
||||
};
|
||||
return documentedFrameCounts[seconds] || (seconds * AGNES_VIDEO_FRAME_RATE + 1);
|
||||
}
|
||||
|
||||
export function getAgnesModelCapabilities(modelName?: string): ModelCapabilityConfig | undefined {
|
||||
const normalizedModelName = String(modelName || '').toLowerCase();
|
||||
return [
|
||||
...AGNES_IMAGE_MODEL_TEMPLATES,
|
||||
...AGNES_VIDEO_MODEL_TEMPLATES,
|
||||
].find(template => template.modelName.toLowerCase() === normalizedModelName)?.capabilities;
|
||||
}
|
||||
|
||||
const agnesImageResolutions = [
|
||||
option('1024x768', '横版 1024x768'),
|
||||
option('1024x1024', '正方形 1024x1024'),
|
||||
@@ -98,7 +110,7 @@ export const AGNES_VIDEO_MODEL_TEMPLATES: AgnesVideoModelTemplate[] = [
|
||||
supportsDuration: true,
|
||||
supportsQuality: false,
|
||||
supportsOutputFormat: false,
|
||||
durations: options(['3', '5', '10', '18']),
|
||||
durations: options(AGNES_STABLE_VIDEO_DURATIONS),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
ensureYuanjieSystemApiManifest,
|
||||
getYuanjieSystemApiCapabilitiesFallback,
|
||||
} from '@/lib/yuanjie-system-manifest';
|
||||
import { getAgnesModelCapabilities } from '@/lib/agnes-model-templates';
|
||||
import type { ManagedVideoUsageMode, ModelCapabilityConfig } from '@/lib/model-config-types';
|
||||
|
||||
export type MembershipTier = 'free' | 'pro' | 'max' | 'ultra';
|
||||
@@ -220,6 +221,13 @@ export function normalizeVideoUsageModes(value: unknown): ManagedVideoUsageMode[
|
||||
return normalized.length > 0 ? normalized : ['text-to-video', 'image-to-video'];
|
||||
}
|
||||
|
||||
export function getAgnesSystemApiCapabilitiesFallback(row: Record<string, unknown>): ModelCapabilityConfig | undefined {
|
||||
const provider = String(row.provider || '').toLowerCase().replace(/\s+/g, '');
|
||||
const modelName = String(row.model_name || '').toLowerCase();
|
||||
if (!provider.includes('agnes') && !modelName.startsWith('agnes-')) return undefined;
|
||||
return getAgnesModelCapabilities(modelName);
|
||||
}
|
||||
|
||||
export function toSafeSystemApi(row: Record<string, unknown>, includeInactive = true): Omit<ServerManagedApiConfig, 'apiKey'> & { apiKey: '' } {
|
||||
return {
|
||||
id: String(row.id || ''),
|
||||
@@ -243,7 +251,7 @@ export function toSafeSystemApi(row: Record<string, unknown>, includeInactive =
|
||||
groupRatio: Number(row.group_ratio || 1),
|
||||
priceNote: String(row.price_note || ''),
|
||||
manifestPath: String(row.manifest_path || ''),
|
||||
capabilities: readManifestCapabilities(String(row.manifest_path || '')) || getYuanjieSystemApiCapabilitiesFallback(row),
|
||||
capabilities: getAgnesSystemApiCapabilitiesFallback(row) || readManifestCapabilities(String(row.manifest_path || '')) || getYuanjieSystemApiCapabilitiesFallback(row),
|
||||
isDefault: row.is_default !== false,
|
||||
allowedMembershipTiers: normalizeAllowedMembershipTiers(row.allowed_membership_tiers),
|
||||
pollingMode: normalizeSystemApiPollingMode(row.polling_mode),
|
||||
|
||||
Reference in New Issue
Block a user