File size: 15,562 Bytes
20a038a
 
 
 
 
 
 
 
0166c45
20a038a
 
 
 
 
 
 
 
 
 
 
 
4c2b737
 
 
 
b29c2ee
 
 
20a038a
4c2b737
 
20a038a
 
 
423f568
a4116d7
 
423f568
 
 
20a038a
 
 
 
 
 
 
 
 
 
610b6f7
52d433d
 
5c10ae6
 
 
 
 
52d433d
5c10ae6
 
 
 
 
4c2b737
5c10ae6
52d433d
5b531bf
52d433d
 
5c10ae6
 
 
 
52d433d
 
 
47a9b22
 
578c515
ea0edd2
7c4d6d8
b29c2ee
8031929
 
 
 
 
 
b29c2ee
 
 
5c10ae6
 
b29c2ee
8031929
 
5c10ae6
 
 
 
 
 
2b51699
26e6258
67e0ec3
52d433d
3b6dabf
4c2b737
52d433d
d3b0c70
 
52d433d
f2217fa
 
d3b0c70
20a038a
2b51699
20a038a
 
 
 
 
 
52d433d
20a038a
 
 
 
 
2b51699
20a038a
 
 
 
 
 
 
 
 
 
 
 
 
76d9175
20a038a
 
 
2b51699
20a038a
 
 
47a9b22
20a038a
 
5c10ae6
 
2b51699
5c10ae6
 
 
ea0edd2
 
5c10ae6
266e15e
26e6258
5c10ae6
20a038a
 
 
 
 
610b6f7
5c10ae6
20a038a
2b51699
20a038a
 
 
 
 
 
 
47a9b22
 
4c2b737
47a9b22
 
266e15e
47a9b22
 
20a038a
 
 
 
364254a
20a038a
 
5b531bf
20a038a
 
7a5cee7
 
 
 
0166c45
20a038a
 
 
52d433d
20a038a
 
 
 
 
 
 
48a4050
0166c45
20a038a
 
 
 
 
 
 
 
5c10ae6
9a70598
 
6080b92
 
9a70598
 
2b51699
9a70598
 
6080b92
 
9a70598
 
20a038a
 
5974295
fe70573
67e0ec3
20a038a
 
 
 
 
 
5c10ae6
2b51699
f8131ff
ce888ba
 
5c10ae6
 
 
4c2b737
9d56ec2
5c10ae6
610b6f7
ceb62f1
52d433d
dbefd15
9d56ec2
dbefd15
ceb62f1
610b6f7
 
 
 
 
2b51699
 
 
 
677f3f6
2b51699
 
610b6f7
 
2b51699
610b6f7
 
2b51699
610b6f7
ceb62f1
610b6f7
52d433d
2b51699
52d433d
5c10ae6
 
 
 
 
52d433d
ceb62f1
dbefd15
4c2b737
 
52d433d
dbefd15
ceb62f1
52d433d
 
95a5a9b
 
52d433d
8d29e19
 
 
 
 
52d433d
63b349a
266e15e
 
 
 
 
 
 
 
52d433d
266e15e
 
 
 
326e30b
266e15e
 
 
 
52d433d
7ed535b
 
52d433d
 
2b51699
610b6f7
 
5c10ae6
 
 
 
52d433d
c9d3aec
 
e6712d2
52d433d
8983acf
8d29e19
52d433d
 
5c10ae6
 
d5a1bcf
5c10ae6
 
 
ea0edd2
5c10ae6
 
ea0edd2
5c10ae6
 
 
 
 
 
52d433d
 
 
5c10ae6
 
 
 
 
 
 
 
266e15e
52d433d
 
 
2b51699
5c10ae6
 
 
 
 
 
ffd9403
63b349a
c9d3aec
 
63b349a
52d433d
 
266e15e
610b6f7
 
 
 
 
52d433d
610b6f7
 
266e15e
 
 
 
 
 
 
 
 
 
63bae32
266e15e
52d433d
8dcd6cc
 
52d433d
2b51699
 
5c10ae6
 
 
 
 
 
52d433d
c9d3aec
 
8dcd6cc
52d433d
8d29e19
 
52d433d
2b51699
ce888ba
5c10ae6
 
 
 
 
 
52d433d
c9d3aec
 
8d29e19
b5f23b5
20a038a
c9d3aec
 
364254a
 
 
 
 
 
5c10ae6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
import os
import gradio as gr
from smolagents import (
    tool,
    CodeAgent,
    DuckDuckGoSearchTool,
    InferenceClientModel,
    FinalAnswerTool,
    LocalPythonExecutor,
)
from huggingface_hub import InferenceClient
import tempfile
from PIL import Image

def pil_to_tempfile(image):
    tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
    tmp_path = tmp.name
    tmp.close()
    image.save(tmp_path, format="PNG")
    return tmp_path

def aligned_num_frames(duration, fps=16):
    n = int(duration * fps)
    return ((n - 1) // 4) * 4 + 1

def align(x, base=16):
    return (x // base) * base

token = os.getenv("HF_TOKEN")
if not token:
    raise RuntimeError("Please set HF_TOKEN environment variable")

client = InferenceClient(token=token)

video_client = InferenceClient(
    model="Wan-AI/Wan2.2-I2V-A14B-Diffusers",
    provider="fal-ai",
    api_key=token,
)

nsfw_image_detection_client = InferenceClient(
    provider="hf-inference",
    api_key=token
)

text_to_image_client = InferenceClient(
    model="stabilityai/stable-diffusion-3-medium",
    api_key=token
)

image_output = None    
video_output = None    

video_prompt = ""
video_duration = 4
video_steps = 20
video_guidance = 3.0

@tool
def video_tool(
    video_image_input: Image.Image, 
    prompt: str = "high quality, detailed, sharp, cinematic", 
    duration: float = 4, 
    steps: int = 20, 
    guidance: float = 3.0,
) -> str:
    """
    Generates a video from a starting image using Wan 2.1.
    Args:
        video_image_input (Image.Image): The source image to be animated.
        prompt (str): The prompt for video generation.
        duration (float): Duration in seconds.
        steps (int): Number of inference steps.
        guidance (float): Guidance scale.
    Returns:
        str: A confirmation message.
    """
    global video_output
    
    try:
        MAX_RES = 640
        w, h = video_image_input.size
        
        scale = min(MAX_RES / w, MAX_RES / h, 1)
        new_w = align(int(w * scale))
        new_h = align(int(h * scale))
        
        image = video_image_input.resize((new_w, new_h), Image.LANCZOS)

        FPS = 16 
        num_frames = aligned_num_frames(duration, FPS)
        
        def generate_video():
            return video_client.image_to_video(
                image=image, 
                width=new_w,
                height=new_h,
                prompt=prompt,
                negative_prompt="low quality, deformed, grainy, blurry, pixelated",
                num_frames=num_frames,
                num_inference_steps=steps,  
                guidance_scale=guidance, 
                decode_chunk_size=8, 
            )        
        video_bytes = generate_video()
        
        out = tempfile.mktemp(suffix=".mp4")
        with open(out, "wb") as f:
            f.write(video_bytes)      
        video_output = out
        return "Video successfully generated and stored for Gradio UI."
        
    except Exception as e:
        video_output = None
        return f"Video generation failed: {e}"
      
@tool
def nsfw_detection_tool(nsfw_detection_input: Image.Image,) -> str:
    """
    Suitable for filtering through score explicit or inappropriate content in images.
    Args:
        nsfw_detection_input (Image.Image): The image to check.
    Returns:
        str: Highest score result.
    """
    try:
        tmp_path = pil_to_tempfile(nsfw_detection_input)
        outputs = client.image_classification(
            tmp_path,
            model="Falconsai/nsfw_image_detection"
        )        
        os.remove(tmp_path)

        top_result = max(outputs, key=lambda x: x.score)
        verdict = (
            f"Verdict: {top_result.label.upper()}\n"
            f"Confidence: {top_result.score:.2%}"
        )
        return verdict

    except Exception as e:
        return f"NSFW detection failed: {e}"

@tool
def image_tool(image_prompt_param: str,) -> str:
    """
    Generate an image from text using SD3-Medium.
    Args:
        image_prompt_param (str): image description.
    Returns:
        str: A confirmation message.
    """    
    global image_output  
    
    try:
        def generate_image():
            return text_to_image_client.text_to_image(
                prompt=image_prompt_param,
                negative_prompt="low quality, deformed",
                guidance_scale=7.0,
                num_inference_steps=28,
                width=800,
                height=1280
            )
        
        image = generate_image()
        image_output = image
        return "Image successfully generated and stored for Gradio UI."
        
    except Exception as e:
        image_output = None
        return f"Image generation failed: {e}"


@tool
def search_tool(query: str,) -> str:
    """
    Search the web and return the most relevant results.
    Args:
        query (str): The search query.
    Returns:
        str: The search results.
    """
    try:
        web_search_tool = DuckDuckGoSearchTool(max_results=5, rate_limit=2.0)

        results = web_search_tool(query)
        return results
        
    except Exception as e:
        return f"Search failed: {e}" 

final_answer = FinalAnswerTool()

model = InferenceClientModel(
    model_id="meta-llama/Llama-3.3-70B-Instruct",
    token=token,
    max_tokens=2096,
    temperature=0.6,
)

executor = LocalPythonExecutor(
    timeout_seconds=300,
    additional_authorized_imports=[], 
) 

agent = CodeAgent(
    model=model,
    tools=[
        video_tool,
        image_tool,
        nsfw_detection_tool,
        search_tool,
        final_answer,
    ],
    max_steps=6,
    planning_interval=None,
    verbosity_level=2,
    executor=executor,
)

agent.prompt_templates["system_prompt"] += """
    You are a tool calling agent.
    You have access to these tools: 
    - search_tool(query: str) -> str
    - Search the web and return the most relevant results.
    - Used for sentiment analysis
    - video_tool(video_image_input: Image.Image, prompt: str, duration: float, steps: int, guidance: float) -> str
    - Generate a video from an image input with custom parameters, 
    - if successfull or not you will be notified by the return string,
    - you do not need to save the video or print the result, 
    - it will be passed to the gradio ui automatically via the global variable from within the tool,
    - the video_tool has a timeout of 300 so you must be patient,
    - it is running in the background and may take longer thgan 30 seconds.
    - image_tool(image_prompt_param: str) -> str
    - Generate an image from a text prompt,
    - if successfull or not you will be notified by the return string,
    - you do not need to save the image or print the result, 
    - it will be passed to the gradio ui automatically via the global variable from within the tool,
    - the image_tool has a timeout of 300 so you must be patient,
    - it is running in the background and may take longer thgan 30 seconds.
    - nsfw_detection_tool(nsfw_detection_input: Image.Image) -> str
    - The nsfw_detection_input additional argument is processed entirely within the tool to produce a score from the input.
    - When sentiment analysis is requested, you must analyze the sentiment of prompt text using a range score of 0 -> 10 
    - and provied alternative wording.
    - When generating a video, to save time the image must not use the nsfw_detection_tool first.
    - You must construct a well-formatted human-readable answer
    - You must introduce yourself as Jerry and greet the user in the answer
    - You must try include newlines, bullets, numbering, and proper punctuation
    - You must use this answer in final_answer
"""

def run_agent(
    query,
    image_prompt_param="",
    nsfw_detection_input=None, 
    video_image_input=None, 
    video_prompt_param="", 
    video_duration_param=4.0, 
    video_steps_param=20, 
    video_guidance_param=3.0,
    progress=gr.Progress(),
):
    global image_output, video_output
    image_output = None
    video_output = None

    progress(0, desc="Jerry is thinking …")

    try:

        actual_query = ""
        
        if query and query.strip():
            actual_query = query
            progress(0.05, desc="Performing steps..")
        elif image_prompt_param and image_prompt_param.strip():
            actual_query = "Generate an image"
            progress(0.05, desc="Generating image..")
        elif video_image_input is not None:
            actual_query = "Generate a video"
            progress(0.05, desc="Performing diffusion… this might take awhile..")
        elif nsfw_detection_input is not None:
            actual_query = "Check this image for NSFW content"
            progress(0.05, desc="Checking image for NSFW content..")
        else:
            actual_query = "What can I help you with?"
            progress(0.05, desc="Performing steps..")

        response = agent.run(
            actual_query,
            additional_args={
                "image_prompt_param": image_prompt_param,
                "nsfw_detection_input": nsfw_detection_input, 
                "video_image_input": video_image_input,
                "prompt": video_prompt_param,
                "duration": video_duration_param,
                "steps": video_steps_param,
                "guidance": video_guidance_param,
            }
        )

        progress(1, desc="Done…")

        yield image_output, video_output, str(response)

    except Exception as e:
        yield None, None, f"❌ Agent Error: {str(e)}"

with gr.Blocks(title="Jerry AI Assistant") as demo:
    gr.Markdown("# 🤖 Jerry - Your AI Assistant")

    agent_response = gr.Textbox(
        label="Response",
        lines=5,
        interactive=False
    )

    with gr.Tab("💬 Chat"):
        with gr.Row():
            query_chat = gr.Textbox(
                lines=3,
                label="Ask me anything...",
            )
            
        with gr.Row():
            run_chat_btn = gr.Button("🚀 Run", variant="primary")

        gr.Examples(
            examples=[
                "How do i cook a curry quickly",
                "Analyze the sentiment: This is terrible service",
                "Translate this text to English 在线中文输入",
            ],
            inputs=[query_chat],
            label="💡 Try these:"
        )

        run_chat_btn.click(
            fn=run_agent,
            inputs=[
                query_chat,
                gr.Textbox(visible=False),
                gr.Image(visible=False),
                gr.Image(visible=False),
                gr.Textbox(visible=False),
                gr.Number(visible=False),
                gr.Number(visible=False),
                gr.Number(visible=False),
            ],
            outputs=[gr.Image(visible=False), gr.Video(visible=False), agent_response],
            concurrency_limit=5
        )

    with gr.Tab("🎬 Video Tools"):
        with gr.Row():
            with gr.Column():
                video_image_input = gr.Image(type="pil", label="Input Image")
                gr.Markdown("Upload the starting image for the video. This will be animated according to your prompt.")
    
                prompt_txt = gr.Textbox(lines=3, label="Prompt")
                gr.Markdown("Describe what you want in the video. Be as detailed as needed.")
    
                with gr.Accordion("Settings", open=True):
                    dur_slider = gr.Slider(1, 4, value=4, step=0.1, label="Duration (seconds)")
                    gr.Markdown("Controls the length of the video. Longer durations generate more frames and require more compute.")
                    
                    step_slider = gr.Slider(4, 35, value=35, step=1, label="Steps (quality)")
                    gr.Markdown("Number of diffusion steps used per frame. Higher values improve detail and temporal stability but increase generation time.")
                    
                    guidance_slider = gr.Slider(1.0, 6.0, value=3.0, step=0.1, label="Guidance Strength")
                    gr.Markdown("How strongly the model follows the prompt. Lower values are more natural and fluid, higher values are more literal and stylized.")
    
                gen_btn = gr.Button("Generate Video", variant="primary")

            with gr.Column():
                output_vid = gr.Video(label="Generated Video")

        gr.Examples(
            examples=[
                "The people all raise a glass and cheer",
            ],
            inputs=[prompt_txt],
            label="💡 Try these:"
        )
        
        gen_btn.click(
            fn=run_agent,
            inputs=[
                gr.Textbox(visible=False),
                gr.Textbox(visible=False), 
                gr.Image(visible=False),    
                video_image_input,            
                prompt_txt,                 
                dur_slider,                  
                step_slider,              
                guidance_slider,                                
            ],
            outputs=[gr.Image(visible=False), output_vid, agent_response],
            concurrency_limit=5
        )

    with gr.Tab("🎨 Image Tools"):
        with gr.Row():
            with gr.Column():
                nsfw_detection_input = gr.Image(type="pil", label="Upload for NSFW Check")
                check_nsfw_btn = gr.Button("🔍 Check NSFW")
                query_img = gr.Textbox(lines=2, label="Image generation prompt")
                run_img_btn = gr.Button("🎨 Generate Image", variant="primary")

            with gr.Column():
                image_output_display = gr.Image(label="Generated Image")

        gr.Examples(
            examples=[
                "A cyberpunk cat with neon glowing eyes",
                "A serene Japanese garden with cherry blossoms",
                "A futuristic city with flying cars at sunset",
                "A magical forest with bioluminescent plants",
                "A steampunk robot drinking tea in a Victorian parlor"
            ],
            inputs=[query_img],
            label="💡 Try these:"
        )

        check_nsfw_btn.click(
            fn=run_agent,
            inputs=[
                gr.Textbox(visible=False),
                gr.Textbox(visible=False),
                nsfw_detection_input,         
                gr.Image(visible=False),    
                gr.Textbox(visible=False),  
                gr.Number(visible=False),    
                gr.Number(visible=False),    
                gr.Number(visible=False),    
            ],
            outputs=[gr.Image(visible=False), gr.Video(visible=False), agent_response],
            concurrency_limit=5
        )

        run_img_btn.click(
            fn=run_agent,
            inputs=[
                gr.Textbox(visible=False),
                query_img,
                gr.Image(visible=False),      
                gr.Image(visible=False),      
                gr.Textbox(visible=False),   
                gr.Number(visible=False),     
                gr.Number(visible=False),     
                gr.Number(visible=False),      
            ],
            outputs=[image_output_display, gr.Video(visible=False), agent_response],
            concurrency_limit=5
        )
        
if __name__ == "__main__":
    demo.queue()

    demo.launch(
        server_name="0.0.0.0", 
        server_port=7860, 
        theme=gr.themes.Soft(),
        show_error=True,
        max_threads=10 
    )