生成任务兼容图片类型
This commit is contained in:
@@ -0,0 +1,143 @@
|
|||||||
|
# =========================
|
||||||
|
# Dependencies
|
||||||
|
# =========================
|
||||||
|
#node_modules/
|
||||||
|
.pnp/
|
||||||
|
.pnp.js
|
||||||
|
.pnp.cjs
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# Build outputs
|
||||||
|
# =========================
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
out/
|
||||||
|
.next/
|
||||||
|
.nuxt/
|
||||||
|
.cache/
|
||||||
|
.parcel-cache/
|
||||||
|
.vite/
|
||||||
|
.turbo/
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# Logs
|
||||||
|
# =========================
|
||||||
|
logs/
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# Runtime / cache
|
||||||
|
# =========================
|
||||||
|
.cache/
|
||||||
|
.eslintcache
|
||||||
|
.stylelintcache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# Environment files
|
||||||
|
# =========================
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
# 如果你有示例环境变量文件,可以提交
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# WebStorm / JetBrains IDE
|
||||||
|
# =========================
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
*.iws
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
# 如果团队需要共享 WebStorm 配置,可以改成下面这种策略:
|
||||||
|
# .idea/*
|
||||||
|
# !.idea/codeStyles/
|
||||||
|
# !.idea/inspectionProfiles/
|
||||||
|
# !.idea/runConfigurations/
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# VSCode
|
||||||
|
# =========================
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
!.vscode/settings.json
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# OS files
|
||||||
|
# =========================
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
Desktop.ini
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# Test coverage
|
||||||
|
# =========================
|
||||||
|
coverage/
|
||||||
|
.nyc_output/
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# Storybook
|
||||||
|
# =========================
|
||||||
|
storybook-static/
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# Cypress / Playwright
|
||||||
|
# =========================
|
||||||
|
cypress/videos/
|
||||||
|
cypress/screenshots/
|
||||||
|
playwright-report/
|
||||||
|
test-results/
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# Sentry / source maps
|
||||||
|
# =========================
|
||||||
|
*.map
|
||||||
|
|
||||||
|
# 如果线上需要 sourcemap,就不要忽略 *.map
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# Package manager cache
|
||||||
|
# =========================
|
||||||
|
.npm/
|
||||||
|
.yarn/*
|
||||||
|
!.yarn/releases
|
||||||
|
!.yarn/plugins
|
||||||
|
!.yarn/sdks
|
||||||
|
!.yarn/versions
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# Temporary files
|
||||||
|
# =========================
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# Local certificates / keys
|
||||||
|
# =========================
|
||||||
|
*.pem
|
||||||
|
*.key
|
||||||
|
*.crt
|
||||||
|
*.p12
|
||||||
|
*.pfx
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# Misc
|
||||||
|
# =========================
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
@@ -298,7 +298,7 @@ export async function adminUpdateGenerationStatus(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function adminGenerateVideo(
|
export async function adminGenerateVideo(
|
||||||
recordId: string, aspectRatio: string, resolution: string
|
recordId: string, aspectRatio: string, resolution: string, image_size: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await api.post(`/admin/generation-records/${recordId}/generate`, { aspect_ratio: aspectRatio, resolution });
|
await api.post(`/admin/generation-records/${recordId}/generate`, { aspect_ratio: aspectRatio, resolution, image_size });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
PlayCircleOutlined, EyeOutlined, ClockCircleOutlined, CheckCircleOutlined,
|
PlayCircleOutlined, EyeOutlined, ClockCircleOutlined, CheckCircleOutlined,
|
||||||
LoadingOutlined, CloseCircleOutlined, SearchOutlined, VideoCameraOutlined,
|
LoadingOutlined, CloseCircleOutlined, SearchOutlined, VideoCameraOutlined,
|
||||||
ExclamationCircleOutlined,
|
ExclamationCircleOutlined, FileImageOutlined
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { getAdminGenerationRecords, adminUpdateGenerationStatus, adminGenerateVideo } from '../api';
|
import { getAdminGenerationRecords, adminUpdateGenerationStatus, adminGenerateVideo } from '../api';
|
||||||
import type { AdminGenerationRecord } from '../types';
|
import type { AdminGenerationRecord } from '../types';
|
||||||
@@ -32,7 +32,7 @@ const AdminGenerationRecords: React.FC = () => {
|
|||||||
const [filterUserId, setFilterUserId] = useState<string>('');
|
const [filterUserId, setFilterUserId] = useState<string>('');
|
||||||
const [preview, setPreview] = useState<AdminGenerationRecord | null>(null);
|
const [preview, setPreview] = useState<AdminGenerationRecord | null>(null);
|
||||||
const [updating, setUpdating] = useState<string | null>(null);
|
const [updating, setUpdating] = useState<string | null>(null);
|
||||||
const [genModal, setGenModal] = useState<{ record: AdminGenerationRecord; ratio: string; resolution: string } | null>(null);
|
const [genModal, setGenModal] = useState<{ record: AdminGenerationRecord; ratio: string; resolution: string; image_size: string } | null>(null);
|
||||||
|
|
||||||
const load = async () => {
|
const load = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -63,7 +63,12 @@ const AdminGenerationRecords: React.FC = () => {
|
|||||||
videoTokensUsed: item.videoTokensUsed || 0,
|
videoTokensUsed: item.videoTokensUsed || 0,
|
||||||
errorMessage: item.errorMessage,
|
errorMessage: item.errorMessage,
|
||||||
createdAt: item.createdAt,
|
createdAt: item.createdAt,
|
||||||
generatedAt: item.generatedAt,
|
genType: item.genType,
|
||||||
|
imageSize: item.imageSize,
|
||||||
|
imageUrl: item.imageUrl,
|
||||||
|
imageTokensUsed: item.imageTokensUsed,
|
||||||
|
imageProportion: item.imageProportion,
|
||||||
|
imagePx: item.imagePx,
|
||||||
})));
|
})));
|
||||||
setTotal(res.total);
|
setTotal(res.total);
|
||||||
} catch {
|
} catch {
|
||||||
@@ -92,7 +97,7 @@ const AdminGenerationRecords: React.FC = () => {
|
|||||||
if (!genModal) return;
|
if (!genModal) return;
|
||||||
setUpdating(genModal.record.id);
|
setUpdating(genModal.record.id);
|
||||||
try {
|
try {
|
||||||
await adminGenerateVideo(genModal.record.id, genModal.ratio, genModal.resolution);
|
await adminGenerateVideo(genModal.record.id, genModal.ratio, genModal.resolution, genModal.image_size);
|
||||||
message.success('已提交视频生成');
|
message.success('已提交视频生成');
|
||||||
setGenModal(null);
|
setGenModal(null);
|
||||||
load();
|
load();
|
||||||
@@ -117,6 +122,12 @@ const AdminGenerationRecords: React.FC = () => {
|
|||||||
title: '项目', dataIndex: 'projectName', width: 120, ellipsis: true,
|
title: '项目', dataIndex: 'projectName', width: 120, ellipsis: true,
|
||||||
render: (v: string) => <Typography.Text style={{ fontSize: 13 }}>{v}</Typography.Text>,
|
render: (v: string) => <Typography.Text style={{ fontSize: 13 }}>{v}</Typography.Text>,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '类型', dataIndex: 'genType', width: 60, ellipsis: true,
|
||||||
|
render: (v: string) => <Typography.Text style={{ fontSize: 13 }}>
|
||||||
|
{v==='video' ? "视频" : "图片"}
|
||||||
|
</Typography.Text>,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '提示词', key: 'prompt', ellipsis: true,
|
title: '提示词', key: 'prompt', ellipsis: true,
|
||||||
render: (_: any, r: AdminGenerationRecord) => (
|
render: (_: any, r: AdminGenerationRecord) => (
|
||||||
@@ -130,13 +141,22 @@ const AdminGenerationRecords: React.FC = () => {
|
|||||||
{
|
{
|
||||||
title: '参数', key: 'params', width: 140,
|
title: '参数', key: 'params', width: 140,
|
||||||
render: (_: any, r: AdminGenerationRecord) => (
|
render: (_: any, r: AdminGenerationRecord) => (
|
||||||
r.duration ? (
|
r.genType === 'video'?
|
||||||
|
(r.duration ? (
|
||||||
<Space size={4} wrap>
|
<Space size={4} wrap>
|
||||||
<Tag>{r.duration}s</Tag>
|
<Tag>{r.duration}s</Tag>
|
||||||
<Tag>{r.aspectRatio}</Tag>
|
<Tag>{r.aspectRatio}</Tag>
|
||||||
<Tag>{r.resolution}</Tag>
|
<Tag>{r.resolution}</Tag>
|
||||||
</Space>
|
</Space>
|
||||||
) : <Tag color="default">待配置</Tag>
|
) : <Tag color="default">待配置</Tag>)
|
||||||
|
:
|
||||||
|
(r.imageSize ? (
|
||||||
|
<Space size={4} wrap>
|
||||||
|
<Tag>{r.imageSize}</Tag>
|
||||||
|
<Tag>{r.imageProportion}</Tag>
|
||||||
|
<Tag>{r.imagePx}</Tag>
|
||||||
|
</Space>
|
||||||
|
) : <Tag color="default">待配置</Tag>)
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -147,7 +167,7 @@ const AdminGenerationRecords: React.FC = () => {
|
|||||||
<div style={{ color: '#f59e0b' }}>文字: {r.textCreditsCost}</div>
|
<div style={{ color: '#f59e0b' }}>文字: {r.textCreditsCost}</div>
|
||||||
)}
|
)}
|
||||||
{r.creditsCost > 0 && (
|
{r.creditsCost > 0 && (
|
||||||
<div style={{ color: '#6366f1' }}>视频: {r.creditsCost}</div>
|
<div style={{ color: '#6366f1' }}>{r.genType === 'video'? "视频" : "图片"}: {r.creditsCost}</div>
|
||||||
)}
|
)}
|
||||||
{r.textCreditsCost === 0 && r.creditsCost === 0 && (
|
{r.textCreditsCost === 0 && r.creditsCost === 0 && (
|
||||||
<Typography.Text style={{ color: '#94a3b8' }}>0</Typography.Text>
|
<Typography.Text style={{ color: '#94a3b8' }}>0</Typography.Text>
|
||||||
@@ -193,16 +213,16 @@ const AdminGenerationRecords: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
{r.status === 'failed' && (
|
{r.status === 'failed' && (
|
||||||
<Button size="small" type="primary" danger loading={updating === r.id}
|
<Button size="small" type="primary" danger loading={updating === r.id}
|
||||||
onClick={() => setGenModal({ record: r, ratio: r.aspectRatio || '16:9', resolution: r.resolution || '720p' })}>
|
onClick={() => setGenModal({ record: r, ratio: r.aspectRatio || '16:9', resolution: r.resolution || '720p', image_size: r.imageSize })}>
|
||||||
重试生成
|
重试生成
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{r.status === 'prompt_optimized' && (
|
{r.status === 'prompt_optimized' && (
|
||||||
<>
|
<>
|
||||||
<Button size="small" type="primary" loading={updating === r.id}
|
<Button size="small" type="primary" loading={updating === r.id}
|
||||||
onClick={() => setGenModal({ record: r, ratio: '16:9', resolution: '720p' })}
|
onClick={() => setGenModal({ record: r, ratio: '16:9', resolution: '720p', image_size: r.imageSize })}
|
||||||
style={{ background: '#6366f1', border: 'none' }}>
|
style={{ background: '#6366f1', border: 'none' }}>
|
||||||
生成视频
|
生成{r.genType === 'video'? "视频" : "图片"}
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="small" danger loading={updating === r.id}
|
<Button size="small" danger loading={updating === r.id}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -328,7 +348,7 @@ const AdminGenerationRecords: React.FC = () => {
|
|||||||
<Typography.Text style={{ fontSize: 11, color: '#94a3b8' }}> ({preview.textTokensUsed} tokens)</Typography.Text>
|
<Typography.Text style={{ fontSize: 11, color: '#94a3b8' }}> ({preview.textTokensUsed} tokens)</Typography.Text>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ flex: 1 }}>
|
<div style={{ flex: 1 }}>
|
||||||
<Typography.Text style={{ fontSize: 11, color: '#94a3b8', display: 'block' }}>视频积分</Typography.Text>
|
<Typography.Text style={{ fontSize: 11, color: '#94a3b8', display: 'block' }}>{preview.genType === 'image'? '图片' : '视频'}积分</Typography.Text>
|
||||||
<Typography.Text strong style={{ fontSize: 14, color: '#6366f1' }}>{preview.creditsCost}</Typography.Text>
|
<Typography.Text strong style={{ fontSize: 14, color: '#6366f1' }}>{preview.creditsCost}</Typography.Text>
|
||||||
{preview.videoTokensUsed > 0 && (
|
{preview.videoTokensUsed > 0 && (
|
||||||
<Typography.Text style={{ fontSize: 11, color: '#94a3b8' }}> ({preview.videoTokensUsed} tokens)</Typography.Text>
|
<Typography.Text style={{ fontSize: 11, color: '#94a3b8' }}> ({preview.videoTokensUsed} tokens)</Typography.Text>
|
||||||
@@ -341,7 +361,8 @@ const AdminGenerationRecords: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Video Params */}
|
{/* Video Params */}
|
||||||
{preview.duration ? (
|
{preview.genType === 'video' && (
|
||||||
|
preview.duration ? (
|
||||||
<div style={{ display: 'flex', gap: 16, padding: 12, borderRadius: 10, background: '#f8f9fc' }}>
|
<div style={{ display: 'flex', gap: 16, padding: 12, borderRadius: 10, background: '#f8f9fc' }}>
|
||||||
{[
|
{[
|
||||||
{ label: '时长', value: `${preview.duration}秒` },
|
{ label: '时长', value: `${preview.duration}秒` },
|
||||||
@@ -358,6 +379,29 @@ const AdminGenerationRecords: React.FC = () => {
|
|||||||
<div style={{ padding: 12, borderRadius: 10, background: '#f8f9fc', textAlign: 'center' }}>
|
<div style={{ padding: 12, borderRadius: 10, background: '#f8f9fc', textAlign: 'center' }}>
|
||||||
<Tag color="default">视频参数待用户配置</Tag>
|
<Tag color="default">视频参数待用户配置</Tag>
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Image Params */}
|
||||||
|
{preview.genType === 'image' && (
|
||||||
|
preview.imageSize ? (
|
||||||
|
<div style={{ display: 'flex', gap: 16, padding: 12, borderRadius: 10, background: '#f8f9fc' }}>
|
||||||
|
{[
|
||||||
|
{ label: '尺寸', value: preview.imagePx },
|
||||||
|
{ label: '比例', value: preview.imageProportion },
|
||||||
|
{ label: '分辨率', value: preview.imageSize },
|
||||||
|
].map((item, i) => (
|
||||||
|
<div key={i} style={{ flex: 1 }}>
|
||||||
|
<Typography.Text style={{ fontSize: 11, color: '#94a3b8', display: 'block' }}>{item.label}</Typography.Text>
|
||||||
|
<Typography.Text strong style={{ fontSize: 14 }}>{item.value}</Typography.Text>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div style={{ padding: 12, borderRadius: 10, background: '#f8f9fc', textAlign: 'center' }}>
|
||||||
|
<Tag color="default">图片参数待用户配置</Tag>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Reference images */}
|
{/* Reference images */}
|
||||||
@@ -398,6 +442,18 @@ const AdminGenerationRecords: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Image preview */}
|
||||||
|
{preview.status === 'completed' && preview.imageUrl && preview.genType === 'image' && (
|
||||||
|
<div>
|
||||||
|
<Typography.Text style={{ fontSize: 12, color: '#94a3b8', display: 'block', marginBottom: 6 }}>生成图片</Typography.Text>
|
||||||
|
<img
|
||||||
|
// src={apiUrl(preview.imageUrl)}
|
||||||
|
src={`http://ceshi.apiforeign.minzhong.cn${preview.imageUrl}`}
|
||||||
|
style={{ width: '100%', minHeight: 100, borderRadius: 12, background: '#000' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Error message */}
|
{/* Error message */}
|
||||||
{preview.status === 'failed' && preview.errorMessage && (
|
{preview.status === 'failed' && preview.errorMessage && (
|
||||||
<div style={{ padding: 12, borderRadius: 10, background: 'rgba(239,68,68,0.04)', border: '1px solid rgba(239,68,68,0.15)' }}>
|
<div style={{ padding: 12, borderRadius: 10, background: 'rgba(239,68,68,0.04)', border: '1px solid rgba(239,68,68,0.15)' }}>
|
||||||
@@ -416,7 +472,7 @@ const AdminGenerationRecords: React.FC = () => {
|
|||||||
|
|
||||||
{/* Generate modal */}
|
{/* Generate modal */}
|
||||||
<Modal
|
<Modal
|
||||||
title={<Space><PlayCircleOutlined />生成视频</Space>}
|
title={<Space>{genModal && (genModal.record.genType === 'video' ? <PlayCircleOutlined /> : <FileImageOutlined />)}{genModal && (genModal.record.genType === 'video' ? '生成视频' : '生成图片')}</Space>}
|
||||||
open={!!genModal}
|
open={!!genModal}
|
||||||
onCancel={() => setGenModal(null)}
|
onCancel={() => setGenModal(null)}
|
||||||
onOk={handleGenerate}
|
onOk={handleGenerate}
|
||||||
@@ -427,6 +483,8 @@ const AdminGenerationRecords: React.FC = () => {
|
|||||||
>
|
>
|
||||||
{genModal && (
|
{genModal && (
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16, marginTop: 16 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 16, marginTop: 16 }}>
|
||||||
|
{genModal.record.genType === 'video' ?
|
||||||
|
<>
|
||||||
<div style={{ padding: 12, borderRadius: 10, background: '#f8f9fc' }}>
|
<div style={{ padding: 12, borderRadius: 10, background: '#f8f9fc' }}>
|
||||||
<Typography.Text style={{ fontSize: 11, color: '#94a3b8', display: 'block' }}>时长</Typography.Text>
|
<Typography.Text style={{ fontSize: 11, color: '#94a3b8', display: 'block' }}>时长</Typography.Text>
|
||||||
<Typography.Text strong>{genModal.record.duration || 5}s</Typography.Text>
|
<Typography.Text strong>{genModal.record.duration || 5}s</Typography.Text>
|
||||||
@@ -445,6 +503,25 @@ const AdminGenerationRecords: React.FC = () => {
|
|||||||
options={['480p', '720p', '1080p'].map(r => ({ value: r, label: r }))}
|
options={['480p', '720p', '1080p'].map(r => ({ value: r, label: r }))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</> :
|
||||||
|
<>
|
||||||
|
<div style={{ padding: 12, borderRadius: 10, background: '#f8f9fc' }}>
|
||||||
|
<Typography.Text style={{ fontSize: 11, color: '#94a3b8', display: 'block' }}>尺寸</Typography.Text>
|
||||||
|
<Typography.Text strong>{genModal.record.imagePx}</Typography.Text>
|
||||||
|
</div>
|
||||||
|
<div style={{ padding: 12, borderRadius: 10, background: '#f8f9fc' }}>
|
||||||
|
<Typography.Text style={{ fontSize: 11, color: '#94a3b8', display: 'block' }}>比例</Typography.Text>
|
||||||
|
<Typography.Text strong>{genModal.record.imageProportion}</Typography.Text>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Typography.Text style={{ fontSize: 12, color: '#64748b', display: 'block', marginBottom: 6 }}>分辨率</Typography.Text>
|
||||||
|
<Select value={genModal.image_size} onChange={(v) => setGenModal(prev => prev ? { ...prev, image_size: v } : null)}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
options={['2K', '4K'].map(r => ({ value: r, label: r }))}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</> }
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@@ -168,4 +168,10 @@ export interface AdminGenerationRecord {
|
|||||||
errorMessage?: string;
|
errorMessage?: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
generatedAt?: string;
|
generatedAt?: string;
|
||||||
|
genType?: string,
|
||||||
|
imageSize?: string,
|
||||||
|
imageUrl?: string,
|
||||||
|
imageTokensUsed?: number,
|
||||||
|
imageProportion?: string,
|
||||||
|
imagePx?: string,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user