FHC_OCR / app.py
Shubadecka's picture
increasing max tokens
e0e14c2
Raw
History Blame Contribute Delete
5.73 kB
import base64
import io
import warnings
# Gradio still references Starlette's deprecated HTTP_422_UNPROCESSABLE_ENTITY.
warnings.filterwarnings(
"ignore",
message="'HTTP_422_UNPROCESSABLE_ENTITY' is deprecated",
category=DeprecationWarning,
)
# Stale installs or old Space builds may still import duckduckgo_search; we use Brave only.
warnings.filterwarnings(
"ignore",
message=r".*`duckduckgo_search`.*renamed.*`ddgs`.*",
category=RuntimeWarning,
)
import gradio as gr
from huggingface_hub import InferenceClient
from PIL import Image
from agent import run_agent
from agent.orchestrator import strip_qwen_thinking
MODEL = "Qwen/Qwen3-VL-30B-A3B-Thinking"
DEFAULT_SYSTEM = (
"You are a helpful, multimodal AI assistant. When an image is sent, simply transcribe it without analysis, unless the user asks for analysis." \
"When you need up-to-date information from the internet, use the search_web tool. Once you have a complete answer, call final_output with the full answer." \
"If a task is impossible or unsafe, call abort with a brief reason."
)
def _image_to_data_url(image_path: str, max_side: int = 1120, quality: int = 85) -> str:
"""Resize and base64-encode a local image so the HF payload stays under the limit."""
with Image.open(image_path) as img:
img = img.convert("RGB")
w, h = img.size
if max(w, h) > max_side:
scale = max_side / max(w, h)
img = img.resize((int(w * scale), int(h * scale)), Image.LANCZOS)
buf = io.BytesIO()
img.save(buf, format="JPEG", quality=quality)
return "data:image/jpeg;base64," + base64.b64encode(buf.getvalue()).decode()
def _normalize_content_for_api(content):
"""
Gradio multimodal history uses {'type': 'file', 'file': FileData}; the HF
chat API expects {'type': 'image_url', 'image_url': {'url': ...}}.
"""
if isinstance(content, str):
return content
if not isinstance(content, list):
return content
out: list = []
for part in content:
if isinstance(part, str):
out.append({"type": "text", "text": part})
continue
if not isinstance(part, dict):
continue
ptype = part.get("type")
if ptype == "text":
out.append({"type": "text", "text": part.get("text", "")})
elif ptype == "file":
fd = part.get("file")
if isinstance(fd, dict) and fd.get("path"):
out.append(
{
"type": "image_url",
"image_url": {"url": _image_to_data_url(fd["path"])},
}
)
elif ptype == "image_url":
out.append(part)
if not out:
return ""
if len(out) == 1 and out[0].get("type") == "text":
return out[0].get("text", "")
return out
def _strip_thinking_from_normalized(content):
"""Remove Qwen thinking tags from text Gradio stored on assistant turns."""
if isinstance(content, str):
return strip_qwen_thinking(content)
if isinstance(content, list):
out = []
for part in content:
if isinstance(part, dict) and part.get("type") == "text":
t = part.get("text", "")
out.append({**part, "text": strip_qwen_thinking(t)})
else:
out.append(part)
return out
return content
def _normalize_history_message(msg: dict) -> dict:
role = msg.get("role")
if role not in ("user", "assistant", "system"):
return msg
content = msg.get("content")
normalized = _normalize_content_for_api(content)
if role == "assistant":
normalized = _strip_thinking_from_normalized(normalized)
return {**msg, "content": normalized}
def respond(
message,
history: list[dict],
system_message,
max_tokens,
temperature,
top_p,
hf_token: gr.OAuthToken,
):
client = InferenceClient(token=hf_token.token, model=MODEL)
# multimodal=True sends {"text": str, "files": [path, ...]}
# Guard against plain strings in case of edge-case history replay
if isinstance(message, dict):
text = message.get("text", "")
files = message.get("files", [])
else:
text = message or ""
files = []
if files:
content = []
if text:
content.append({"type": "text", "text": text})
for f in files:
content.append({"type": "image_url", "image_url": {"url": _image_to_data_url(f)}})
else:
content = text
messages = [{"role": "system", "content": system_message}]
messages.extend(_normalize_history_message(m) for m in history)
messages.append({"role": "user", "content": content})
answer = run_agent(
messages=messages,
client=client,
model=MODEL,
max_tokens=max_tokens,
temperature=temperature,
top_p=top_p,
)
partial = ""
for i in range(0, len(answer), 8):
partial += answer[i : i + 8]
yield partial
chatbot = gr.ChatInterface(
respond,
multimodal=True,
chatbot=gr.Chatbot(height=700),
additional_inputs=[
gr.Textbox(value=DEFAULT_SYSTEM, label="System message"),
gr.Slider(minimum=1, maximum=16384, value=16384, step=1, label="Max new tokens"),
gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label="Temperature"),
gr.Slider(minimum=0.1, maximum=1.0, value=0.95, step=0.05, label="Top-p (nucleus sampling)"),
],
)
with gr.Blocks() as demo:
with gr.Sidebar():
gr.LoginButton()
chatbot.render()
if __name__ == "__main__":
demo.launch(ssr_mode=False)