Add tokens that will pull files with extensions git status
This commit is contained in:
1 parent
e72468b456
commit
7c6b168c2c
11 files changed
+55
-11
No files matched your search
@@ -3,6 +3,7 @@ from collections import OrderedDict
|
||||
|
||||
import sqlite3
|
||||
import os
|
||||
import re
|
||||
from typing import Literal, Optional
|
||||
import threading
|
||||
import time
|
||||
@@ -18,6 +19,9 @@ from toolkit.train_tools import get_torch_dtype
|
||||
|
||||
AITK_Status = Literal["running", "stopped", "error", "completed"]
|
||||
|
||||
# prompt special token: <caption.ext> -> contents of the media file's sibling <stem>.ext
|
||||
_CAPTION_TOKEN_RE = re.compile(r"<caption\.([A-Za-z0-9_.-]+)>")
|
||||
|
||||
|
||||
class CaptionConfig:
|
||||
def __init__(self, **kwargs):
|
||||
@@ -152,6 +156,23 @@ class BaseCaptioner(BaseExtensionProcess):
|
||||
image = image.resize((new_width, new_height), resample=Image.BICUBIC)
|
||||
return image
|
||||
|
||||
def resolve_prompt(self, prompt: Optional[str], file_path: str) -> Optional[str]:
|
||||
"""Expand special tokens in a caption prompt for one media file. <caption.ext>
|
||||
becomes the text of <stem>.ext next to the file (json included, inserted as-is),
|
||||
or blank when that file does not exist."""
|
||||
if not prompt or "<caption." not in prompt:
|
||||
return prompt
|
||||
stem = os.path.splitext(file_path)[0]
|
||||
|
||||
def sub(m):
|
||||
try:
|
||||
with open(f"{stem}.{m.group(1)}", "r", encoding="utf-8") as f:
|
||||
return f.read().strip()
|
||||
except OSError:
|
||||
return ""
|
||||
|
||||
return _CAPTION_TOKEN_RE.sub(sub, prompt)
|
||||
|
||||
def save_caption_for_file(self, file_path: str, caption: str):
|
||||
filename_no_ext = os.path.splitext(file_path)[0]
|
||||
caption_file_path = f"{filename_no_ext}.{self.caption_config.caption_extension}"
|
||||
|
||||
@@ -58,10 +58,13 @@ class Ideogram4Captioner(Qwen3VLCaptioner):
|
||||
best = (err, p, q)
|
||||
return f"{best[1]}:{best[2]}"
|
||||
|
||||
def build_prompt(self, aspect_ratio: str) -> str:
|
||||
def build_prompt(self, aspect_ratio: str, file_path: Optional[str] = None) -> str:
|
||||
# caption_prompt is the user-editable ADDITIONAL INSTRUCTIONS block,
|
||||
# injected into the fixed system prompt (not the whole prompt).
|
||||
user_instructions = (self.caption_config.caption_prompt or "").strip()
|
||||
user_instructions = self.caption_config.caption_prompt or ""
|
||||
if file_path is not None:
|
||||
user_instructions = self.resolve_prompt(user_instructions, file_path)
|
||||
user_instructions = user_instructions.strip()
|
||||
if not user_instructions:
|
||||
user_instructions = "None."
|
||||
prompt = ideogram4_caption_prompt.replace("{{aspect_ratio}}", aspect_ratio)
|
||||
@@ -131,7 +134,7 @@ class Ideogram4Captioner(Qwen3VLCaptioner):
|
||||
aspect_ratio = self.compute_aspect_ratio(width, height)
|
||||
|
||||
img = self.load_pil_image(file_path, max_res=self.caption_config.max_res)
|
||||
prompt = self.build_prompt(aspect_ratio)
|
||||
prompt = self.build_prompt(aspect_ratio, file_path)
|
||||
|
||||
messages = [
|
||||
{
|
||||
|
||||
@@ -117,7 +117,7 @@ class MossMusicCaptioner(AceStepCaptioner):
|
||||
item["language_inputs"] = self._make_inputs(audio_data, LANGUAGE_PROMPT)
|
||||
if self.caption_config.fixed_caption is None:
|
||||
item["caption_inputs"] = self._make_inputs(
|
||||
audio_data, self.caption_config.caption_prompt
|
||||
audio_data, self.resolve_prompt(self.caption_config.caption_prompt, file_path)
|
||||
)
|
||||
return item
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ class Qwen25OmniCaptioner(BaseCaptioner):
|
||||
"role": "user",
|
||||
"content": [
|
||||
media,
|
||||
{"type": "text", "text": self.caption_config.caption_prompt},
|
||||
{"type": "text", "text": self.resolve_prompt(self.caption_config.caption_prompt, _file_path)},
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
@@ -93,7 +93,7 @@ class Qwen38Captioner(BaseCaptioner):
|
||||
def _prep_media(self, file_path: str) -> dict:
|
||||
"""CPU side of one file (image decode / frame extraction, processor); runs in a
|
||||
worker thread ahead of the GPU."""
|
||||
item = self.llm.prepare_caption_inputs(file_path, self.caption_config.caption_prompt)
|
||||
item = self.llm.prepare_caption_inputs(file_path, self.resolve_prompt(self.caption_config.caption_prompt, file_path))
|
||||
item["file"] = file_path
|
||||
return item
|
||||
|
||||
|
||||
@@ -678,7 +678,7 @@ class Qwen3OmniCaptioner(BaseCaptioner):
|
||||
"role": "user",
|
||||
"content": [
|
||||
media,
|
||||
{"type": "text", "text": self.caption_config.caption_prompt},
|
||||
{"type": "text", "text": self.resolve_prompt(self.caption_config.caption_prompt, _file_path)},
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
@@ -98,7 +98,7 @@ class Qwen3VLCaptioner(BaseCaptioner):
|
||||
"type": "image",
|
||||
"image": img,
|
||||
},
|
||||
{"type": "text", "text": self.caption_config.caption_prompt},
|
||||
{"type": "text", "text": self.resolve_prompt(self.caption_config.caption_prompt, file_path)},
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
@@ -415,6 +415,7 @@ const CaptionSimpleJob: React.FC<Props> = ({ jobConfig, setJobConfig, gpuIDs, se
|
||||
)}
|
||||
<TextAreaInput
|
||||
label="Caption Prompt"
|
||||
docKey="config.process[0].caption.caption_prompt"
|
||||
value={jobConfig.config.process[0].caption.caption_prompt || ''}
|
||||
onChange={value => {
|
||||
setJobConfig(value, 'config.process[0].caption.caption_prompt');
|
||||
|
||||
@@ -19,13 +19,13 @@ export default function DocModal() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onClose={onClose} className="relative z-10">
|
||||
<Dialog open={isOpen} onClose={onClose} className="relative z-[60]">
|
||||
<DialogBackdrop
|
||||
transition
|
||||
className="fixed inset-0 bg-gray-900/75 transition-opacity data-closed:opacity-0 data-enter:duration-300 data-enter:ease-out data-leave:duration-200 data-leave:ease-in"
|
||||
/>
|
||||
|
||||
<div className="fixed inset-0 z-10 w-screen overflow-y-auto">
|
||||
<div className="fixed inset-0 z-[60] w-screen overflow-y-auto">
|
||||
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
<DialogPanel
|
||||
transition
|
||||
|
||||
@@ -407,6 +407,25 @@ const docs: { [key: string]: ConfigDoc } = {
|
||||
</>
|
||||
),
|
||||
},
|
||||
'config.process[0].caption.caption_prompt': {
|
||||
title: 'Caption Prompt',
|
||||
description: (
|
||||
<>
|
||||
The instruction sent to the captioning model for every file. It can contain special tokens that are replaced
|
||||
per file before the prompt is sent:
|
||||
<br />
|
||||
<br />
|
||||
<code><caption.ext></code> — inserts the text of the file next to the media file that has the same name
|
||||
and the given extension. For <code>/data/subfolder/img01.webp</code>, <code><caption.json></code> inserts
|
||||
the contents of <code>/data/subfolder/img01.json</code> and <code><caption.txt></code> inserts{' '}
|
||||
<code>img01.txt</code>. The file is inserted as plain text exactly as it is on disk (json included). If the file
|
||||
does not exist, the token is replaced with nothing.
|
||||
<br />
|
||||
<br />
|
||||
Example: <code>Rewrite this caption as a single sentence: <caption.txt></code>
|
||||
</>
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
export const getDoc = (key: string | null | undefined): ConfigDoc | null => {
|
||||
|
||||
@@ -34,7 +34,7 @@ const extensionsVideo = ['mp4', 'mov', 'webm', 'mkv', 'avi'];
|
||||
|
||||
const defaultExtensions = [...extensionsImage];
|
||||
|
||||
const defaultImageCaptionPrompt = "Caption this image as if you were going to try to generate it with an image generator. Be thurough and describe everything in the image. Be decisive by stating things as they are. Do not say things like \"It appears that\" Or \"possibly\". Start out with things like \"A person on the beach\" or \"A black dragon\". No preamble. Just get to the point.";
|
||||
const defaultImageCaptionPrompt = "Caption this image as if you were going to try to generate it with an image generator. Be thorough and describe everything in the image. Be decisive by stating things as they are. Do not say things like \"It appears that\" Or \"possibly\". Start out with things like \"A person on the beach\" or \"A black dragon\". No preamble. Just get to the point.";
|
||||
|
||||
const defaultVideoCaptionPrompt = "Caption this video as if you were going to try to generate it with a video generator. Describe the visual content, how it moves and changes over time, and the camera work. Also describe the audio, including any speech, music, or sound effects, and transcribe spoken dialogue verbatim in quotes. Be decisive by stating things as they are. Do not say things like \"It appears that\" Or \"possibly\". No preamble. Just get to the point.";
|
||||
|
||||
|
||||
Reference in new issue
Block a user