mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-13 19:20:04 +00:00
🐛 fix: use JSON object for video image reference (#14900)
This commit is contained in:
@@ -21,6 +21,46 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
# Remind contributors when a non-release PR targets `main`.
|
||||
# Day-to-day PRs should target `canary`; `main` is reserved for releases
|
||||
# (see .agents/skills/version-release/SKILL.md). Allowed exceptions:
|
||||
# - PR title matches `🚀 release: v{x.y.z}` (minor release)
|
||||
# - head branch matches `hotfix/*` or `release/*` (patch release)
|
||||
- name: Remind contributor if base branch is not canary
|
||||
if: github.event.action == 'opened' && github.event.pull_request.base.ref == 'main'
|
||||
env:
|
||||
HEAD_REF: ${{ github.event.pull_request.head.ref }}
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
run: |
|
||||
if [[ "$HEAD_REF" == hotfix/* ]] || [[ "$HEAD_REF" == release/* ]]; then
|
||||
echo "✅ Release/hotfix branch ($HEAD_REF) -> main is allowed"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$PR_TITLE" =~ ^🚀[[:space:]]+release: ]]; then
|
||||
echo "✅ Release-titled PR -> main is allowed"
|
||||
exit 0
|
||||
fi
|
||||
echo "⚠️ Non-release PR targets main; posting reminder comment."
|
||||
gh pr comment "$PR_NUMBER" --body "$(cat <<'EOF'
|
||||
👋 Thanks for your contribution!
|
||||
|
||||
This PR currently targets the **`main`** branch, but `main` is reserved for release PRs only. Day-to-day development (features, fixes, refactors, docs, etc.) should target the **`canary`** branch.
|
||||
|
||||
### How to fix
|
||||
|
||||
On the PR page, click **Edit** next to the title, then change the base branch from `main` to `canary`.
|
||||
|
||||
### When targeting `main` is allowed
|
||||
|
||||
- PR title starts with `🚀 release: v{x.y.z}` (minor release)
|
||||
- Head branch matches `hotfix/*` or `release/*` (patch release)
|
||||
|
||||
If your PR fits one of these cases, please ignore this message.
|
||||
EOF
|
||||
)"
|
||||
|
||||
- name: Check if author is a team member
|
||||
id: check-team
|
||||
run: |
|
||||
|
||||
@@ -19,6 +19,12 @@ const mockOptions: CreateVideoOptions = {
|
||||
provider: 'openai',
|
||||
};
|
||||
|
||||
const mockVllmOptions: CreateVideoOptions = {
|
||||
apiKey: 'test-api-key',
|
||||
baseURL: 'http://localhost:8000/v1',
|
||||
provider: 'vllm',
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
@@ -95,7 +101,7 @@ describe('createOpenAICompatibleVideo', () => {
|
||||
expect(body.size).toBe('1920x1080');
|
||||
});
|
||||
|
||||
it('should include imageUrl as input_reference', async () => {
|
||||
it('should include imageUrl as JSON input_reference object', async () => {
|
||||
global.fetch = vi.fn().mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({ id: 'video-task-img' }),
|
||||
@@ -111,6 +117,26 @@ describe('createOpenAICompatibleVideo', () => {
|
||||
|
||||
await createOpenAICompatibleVideo(payload, mockOptions);
|
||||
|
||||
const body = JSON.parse((global.fetch as any).mock.calls[0][1].body);
|
||||
expect(body.input_reference).toEqual({ image_url: 'https://example.com/image.jpg' });
|
||||
});
|
||||
|
||||
it('should preserve string input_reference for non-OpenAI compatible providers', async () => {
|
||||
global.fetch = vi.fn().mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({ id: 'video-task-vllm-img' }),
|
||||
});
|
||||
|
||||
const payload: CreateVideoPayload = {
|
||||
model: 'vllm-omni',
|
||||
params: {
|
||||
prompt: 'Continue this scene',
|
||||
imageUrl: 'https://example.com/image.jpg',
|
||||
},
|
||||
};
|
||||
|
||||
await createOpenAICompatibleVideo(payload, mockVllmOptions);
|
||||
|
||||
const body = JSON.parse((global.fetch as any).mock.calls[0][1].body);
|
||||
expect(body.input_reference).toBe('https://example.com/image.jpg');
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import createDebug from 'debug';
|
||||
import { ModelProvider } from 'model-bank';
|
||||
|
||||
import type {
|
||||
CreateVideoPayload,
|
||||
@@ -115,7 +116,7 @@ export async function pollOpenAICompatibleVideoStatus(
|
||||
* model: string,
|
||||
* prompt: string,
|
||||
* seconds?: string, // OpenAI Sora format (string type)
|
||||
* input_reference?: string | object, // For image-to-video
|
||||
* input_reference?: string | { image_url: string } | { file_id: string }, // For image-to-video
|
||||
* }
|
||||
*
|
||||
* Creates a video generation task and returns immediately with inferenceId.
|
||||
@@ -150,7 +151,10 @@ export async function createOpenAICompatibleVideo(
|
||||
|
||||
// Image-to-video support
|
||||
if (imageUrl) {
|
||||
body['input_reference'] = imageUrl;
|
||||
// OpenAI JSON requests reject bare strings, for example:
|
||||
// `input_reference: "https://example.com/image.jpg"`.
|
||||
body['input_reference'] =
|
||||
options.provider === ModelProvider.OpenAI ? { image_url: imageUrl } : imageUrl;
|
||||
}
|
||||
|
||||
log('OpenAI-compatible video API request body: %O', body);
|
||||
|
||||
Reference in New Issue
Block a user