npv2k1 commited on
Commit
e98a02a
·
1 Parent(s): 1678967

Add Gradio application components for image analysis and configuration

Browse files
.github/workflows/deploy-to-huggingface.yml CHANGED
@@ -14,13 +14,13 @@ jobs:
14
  with:
15
  fetch-depth: 0
16
  lfs: true
17
-
18
  - name: Setup Python
19
  uses: actions/setup-python@v4
20
  with:
21
- python-version: '3.11'
22
- cache: 'pip'
23
-
24
  - name: Config git
25
  run: |
26
  git config --global credential.helper store
@@ -43,8 +43,8 @@ jobs:
43
  SPACE_NAME: ${{ secrets.SPACE_NAME }}
44
  run: |
45
  # Create repository URL
46
- REPO_URL="https://huggingface.co/spaces/$HF_USERNAME/template-python"
47
-
48
  # Add Hugging Face as a remote and push
49
  git remote add space $REPO_URL || git remote set-url space $REPO_URL
50
- git push -f space main
 
14
  with:
15
  fetch-depth: 0
16
  lfs: true
17
+
18
  - name: Setup Python
19
  uses: actions/setup-python@v4
20
  with:
21
+ python-version: "3.11"
22
+ cache: "pip"
23
+
24
  - name: Config git
25
  run: |
26
  git config --global credential.helper store
 
43
  SPACE_NAME: ${{ secrets.SPACE_NAME }}
44
  run: |
45
  # Create repository URL
46
+ REPO_URL="https://huggingface.co/spaces/$HF_USERNAME/llm-image"
47
+
48
  # Add Hugging Face as a remote and push
49
  git remote add space $REPO_URL || git remote set-url space $REPO_URL
50
+ git push -f space main
Dockerfile CHANGED
@@ -32,7 +32,7 @@ ENTRYPOINT []
32
 
33
  RUN python setup.py
34
 
35
- EXPOSE 8080
36
  # Run the FastAPI application by default
37
  # Uses `fastapi dev` to enable hot-reloading when the `watch` sync occurs
38
  # Uses `--host 0.0.0.0` to allow access from outside the container
 
32
 
33
  RUN python setup.py
34
 
35
+ EXPOSE 7860
36
  # Run the FastAPI application by default
37
  # Uses `fastapi dev` to enable hot-reloading when the `watch` sync occurs
38
  # Uses `--host 0.0.0.0` to allow access from outside the container
data/image.jpg ADDED
main.py CHANGED
@@ -10,7 +10,7 @@ def main() -> None:
10
  """Main application entry point."""
11
  try:
12
  print("Start app...")
13
- import src.modules.api
14
 
15
  except Exception as e:
16
  logging.error(f"Application failed to start: {e}", exc_info=True)
 
10
  """Main application entry point."""
11
  try:
12
  print("Start app...")
13
+ import src.modules.gradio_app
14
 
15
  except Exception as e:
16
  logging.error(f"Application failed to start: {e}", exc_info=True)
notebooks/notes.ipynb CHANGED
The diff for this file is too large to render. See raw diff
 
pyproject.toml CHANGED
@@ -1,13 +1,16 @@
1
  [project]
2
- name = "template-python"
3
  version = "0.1.0"
4
  description = "Add your description here"
5
  readme = "README.md"
6
  requires-python = ">=3.11"
7
  dependencies = [
8
  "fastapi[standard]>=0.115.8",
 
9
  "ipykernel>=6.29.5",
 
10
  "numpy>=2.2.3",
 
11
  "pytest>=8.3.4",
12
  ]
13
 
 
1
  [project]
2
+ name = "llm-image"
3
  version = "0.1.0"
4
  description = "Add your description here"
5
  readme = "README.md"
6
  requires-python = ">=3.11"
7
  dependencies = [
8
  "fastapi[standard]>=0.115.8",
9
+ "gradio>=5.3.0",
10
  "ipykernel>=6.29.5",
11
+ "litellm>=1.61.13",
12
  "numpy>=2.2.3",
13
+ "openai>=1.63.2",
14
  "pytest>=8.3.4",
15
  ]
16
 
src/modules/gradio_app/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ import src.modules.gradio_app.index
src/modules/gradio_app/components/__init__.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ """Components for the Gradio application UI"""
2
+
3
+ from .config_panel import create_config_panel
4
+ from .prompt_panel import create_prompt_panel
5
+ from .image_panel import create_image_panel
6
+
7
+ __all__ = ['create_config_panel', 'create_prompt_panel', 'create_image_panel']
src/modules/gradio_app/components/config_panel.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from typing import Tuple
3
+
4
+ from src.modules.gradio_app.utils.api import update_model_list
5
+
6
+ def create_config_panel() -> Tuple[gr.Textbox, gr.Textbox, gr.Dropdown]:
7
+ """Create configuration panel with endpoint, API key and model selection"""
8
+ with gr.Column():
9
+ endpoint_url = gr.Textbox(
10
+ label="LiteLLM Endpoint URL",
11
+ placeholder="Enter your LiteLLM endpoint URL",
12
+ )
13
+ api_key = gr.Textbox(
14
+ label="API Key",
15
+ placeholder="Enter your API key",
16
+ type="password"
17
+ )
18
+ refresh_models = gr.Button("Refresh Models")
19
+ model_dropdown = gr.Dropdown(
20
+ label="Select Model",
21
+ choices=[],
22
+ interactive=True
23
+ )
24
+
25
+ # Update model list when endpoint or API key changes
26
+ def update_models(endpoint_url, api_key):
27
+ models = update_model_list(endpoint_url, api_key)
28
+ return gr.Dropdown(choices=models)
29
+
30
+ refresh_models.click(
31
+ fn=update_models,
32
+ inputs=[endpoint_url, api_key],
33
+ outputs=model_dropdown
34
+ )
35
+
36
+ # Update models automatically when endpoint or API key changes
37
+ endpoint_url.change(
38
+ fn=update_models,
39
+ inputs=[endpoint_url, api_key],
40
+ outputs=model_dropdown
41
+ )
42
+ api_key.change(
43
+ fn=update_models,
44
+ inputs=[endpoint_url, api_key],
45
+ outputs=model_dropdown
46
+ )
47
+
48
+ return endpoint_url, api_key, model_dropdown
src/modules/gradio_app/components/image_panel.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from typing import Tuple
3
+
4
+ def create_image_panel() -> Tuple[gr.Image, gr.Button, gr.Textbox]:
5
+ """Create image upload and analysis panel"""
6
+ with gr.Row():
7
+ with gr.Column():
8
+ image_input = gr.Image(
9
+ label="Upload Image",
10
+ type="pil"
11
+ )
12
+ analyze_button = gr.Button("Analyze Image")
13
+
14
+ with gr.Column():
15
+ output = gr.Textbox(
16
+ label="Analysis Result",
17
+ lines=10
18
+ )
19
+
20
+ return image_input, analyze_button, output
src/modules/gradio_app/components/prompt_panel.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from typing import Tuple
3
+
4
+ def create_prompt_panel() -> Tuple[gr.Textbox, gr.Textbox, gr.Slider]:
5
+ """Create custom prompt panel for image analysis"""
6
+ with gr.Column():
7
+ prompt_input = gr.Textbox(
8
+ label="Custom Prompt",
9
+ placeholder="Enter your prompt for image analysis...",
10
+ lines=3,
11
+ value="Please analyze this image in detail."
12
+ )
13
+ system_prompt = gr.Textbox(
14
+ label="System Prompt (Optional)",
15
+ placeholder="Enter a system prompt to guide the model...",
16
+ lines=2,
17
+ visible=False # Hidden by default
18
+ )
19
+ with gr.Row():
20
+ advanced_checkbox = gr.Checkbox(
21
+ label="Show Advanced Options",
22
+ value=False
23
+ )
24
+ temperature = gr.Slider(
25
+ minimum=0.0,
26
+ maximum=2.0,
27
+ value=1.0,
28
+ step=0.1,
29
+ label="Temperature",
30
+ visible=False
31
+ )
32
+
33
+ def toggle_advanced(show_advanced):
34
+ return {
35
+ system_prompt: gr.update(visible=show_advanced),
36
+ temperature: gr.update(visible=show_advanced)
37
+ }
38
+
39
+ advanced_checkbox.change(
40
+ fn=toggle_advanced,
41
+ inputs=[advanced_checkbox],
42
+ outputs=[system_prompt, temperature]
43
+ )
44
+
45
+ return prompt_input, system_prompt, temperature
src/modules/gradio_app/index.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ from src.modules.gradio_app.components.config_panel import create_config_panel
4
+ from src.modules.gradio_app.components.prompt_panel import create_prompt_panel
5
+ from src.modules.gradio_app.components.image_panel import create_image_panel
6
+ from src.modules.gradio_app.utils.analysis import analyze_image
7
+
8
+ def create_gradio_app():
9
+ """Create the main Gradio application with all components"""
10
+ with gr.Blocks() as app:
11
+ gr.Markdown("# Image Analysis with LLM")
12
+ # Image Analysis Panel
13
+ image_input, analyze_button, output = create_image_panel()
14
+
15
+ # Configuration Panel
16
+ with gr.Row():
17
+ endpoint_url, api_key, model_dropdown = create_config_panel()
18
+
19
+ # Prompt Panel
20
+ with gr.Row():
21
+ prompt_input, system_prompt, temperature = create_prompt_panel()
22
+
23
+
24
+ # Connect the analyze button to the analysis function
25
+ analyze_button.click(
26
+ fn=analyze_image,
27
+ inputs=[
28
+ image_input,
29
+ endpoint_url,
30
+ api_key,
31
+ model_dropdown,
32
+ prompt_input,
33
+ system_prompt,
34
+ temperature
35
+ ],
36
+ outputs=output
37
+ )
38
+
39
+ return app
40
+
41
+ app = create_gradio_app()
42
+
43
+ # Configure server settings for Docker deployment
44
+ server_port = 7860 # Standard Gradio port
45
+ server_name = "0.0.0.0" # Allow external connections
46
+
47
+ def main():
48
+ """Launch the Gradio application"""
49
+ app.launch(
50
+ server_name=server_name,
51
+ server_port=server_port,
52
+ share=False, # Disable sharing as we're running in Docker
53
+ auth=None, # Can be configured if authentication is needed
54
+ ssl_verify=False, # Disable SSL verification for internal Docker network
55
+ show_error=True,
56
+ favicon_path=None
57
+ )
58
+
59
+ main()
src/modules/gradio_app/utils/__init__.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Utility functions for the Gradio application"""
2
+
3
+ from .api import get_available_models, update_model_list
4
+ from .image import encode_image_to_base64
5
+ from .analysis import analyze_image
6
+
7
+ __all__ = [
8
+ 'get_available_models',
9
+ 'update_model_list',
10
+ 'encode_image_to_base64',
11
+ 'analyze_image'
12
+ ]
src/modules/gradio_app/utils/analysis.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+ import openai
3
+ from .image import encode_image_to_base64
4
+
5
+ def analyze_image(
6
+ image,
7
+ endpoint_url: str,
8
+ api_key: str,
9
+ selected_model: str,
10
+ prompt: str,
11
+ system_prompt: Optional[str] = None,
12
+ temperature: float = 1.0
13
+ ):
14
+ """Analyze image using LiteLLM with custom prompts"""
15
+ if not endpoint_url or not api_key:
16
+ return "Please configure both the endpoint URL and API key"
17
+
18
+ if not selected_model:
19
+ return "Please select a model"
20
+
21
+ print(f"Analyzing image with model: {selected_model}")
22
+
23
+ try:
24
+ client = openai.OpenAI(
25
+ api_key=api_key,
26
+ base_url=endpoint_url
27
+ )
28
+
29
+ # Convert image to base64
30
+ base64_image = encode_image_to_base64(image)
31
+
32
+ # Prepare messages
33
+ messages = []
34
+
35
+ # Add system prompt if provided
36
+ if system_prompt:
37
+ messages.append({
38
+ "role": "system",
39
+ "content": system_prompt
40
+ })
41
+
42
+ # Add user message with image
43
+ messages.append({
44
+ "role": "user",
45
+ "content": [
46
+ {"type": "text", "text": prompt},
47
+ {"type": "image_url", "image_url": base64_image}
48
+ ]
49
+ })
50
+
51
+ # Call LiteLLM with custom endpoint
52
+ response = client.chat.completions.create(
53
+ model=selected_model,
54
+ messages=messages,
55
+ temperature=temperature
56
+ )
57
+
58
+ return str(response.choices[0].message.content)
59
+
60
+ except Exception as e:
61
+ return f"Error analyzing image: {str(e)}"
src/modules/gradio_app/utils/api.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List
2
+ import requests
3
+
4
+ def get_available_models(endpoint_url: str, api_key: str) -> List[str]:
5
+ """Fetch available models from LiteLLM endpoint"""
6
+ try:
7
+ response = requests.get(
8
+ f"{endpoint_url.rstrip('/')}/models",
9
+ headers={"Authorization": f"Bearer {api_key}"}
10
+ )
11
+ if response.status_code == 200:
12
+ models = response.json()
13
+ return [model["id"] for model in models["data"]]
14
+ return []
15
+ except Exception as e:
16
+ print(f"Error fetching models: {e}")
17
+ return []
18
+
19
+ def update_model_list(endpoint_url: str, api_key: str) -> List[str]:
20
+ """Update the model list when endpoint or API key changes"""
21
+ if not endpoint_url or not api_key:
22
+ return []
23
+ return get_available_models(endpoint_url, api_key)
src/modules/gradio_app/utils/image.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ from io import BytesIO
3
+ from PIL import Image
4
+
5
+ def encode_image_to_base64(image: Image.Image) -> str:
6
+ """Convert image to base64 string"""
7
+ buffered = BytesIO()
8
+ image.save(buffered, format="PNG")
9
+ img_str = base64.b64encode(buffered.getvalue()).decode()
10
+ return f"data:image/png;base64,{img_str}"
uv.lock CHANGED
The diff for this file is too large to render. See raw diff