langvio 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- langvio/__init__.py +132 -0
- langvio/cli.py +120 -0
- langvio/config.py +190 -0
- langvio/core/__init__.py +0 -0
- langvio/core/base.py +46 -0
- langvio/core/pipeline.py +154 -0
- langvio/core/processor_manager.py +140 -0
- langvio/core/registry.py +130 -0
- langvio/core/visualization_manager.py +392 -0
- langvio/default_config.yaml +107 -0
- langvio/llm/__init__.py +12 -0
- langvio/llm/base.py +279 -0
- langvio/llm/factory.py +101 -0
- langvio/llm/google.py +74 -0
- langvio/llm/openai.py +73 -0
- langvio/media/__init__.py +0 -0
- langvio/media/image_visualizer.py +181 -0
- langvio/media/processor.py +90 -0
- langvio/media/video_visualizer.py +282 -0
- langvio/prompts/__init__.py +11 -0
- langvio/prompts/constants.py +32 -0
- langvio/prompts/templates.py +122 -0
- langvio/utils/__init__.py +0 -0
- langvio/utils/detection/__init__.py +28 -0
- langvio/utils/detection/compression.py +109 -0
- langvio/utils/detection/extraction.py +280 -0
- langvio/utils/file_utils.py +123 -0
- langvio/utils/llm_utils.py +935 -0
- langvio/utils/logging.py +53 -0
- langvio/utils/spatial/__init__.py +16 -0
- langvio/utils/spatial/relationships.py +179 -0
- langvio/vision/__init__.py +0 -0
- langvio/vision/base.py +159 -0
- langvio/vision/color_detection.py +371 -0
- langvio/vision/utils.py +299 -0
- langvio/vision/yolo/__init__.py +0 -0
- langvio/vision/yolo/detector.py +139 -0
- langvio/vision/yolo/image_processor.py +169 -0
- langvio/vision/yolo/result_formatter.py +368 -0
- langvio/vision/yolo/video_processor.py +452 -0
- langvio/vision/yolo/yolo11_utils.py +165 -0
- langvio-0.0.1.dist-info/METADATA +445 -0
- langvio-0.0.1.dist-info/RECORD +47 -0
- langvio-0.0.1.dist-info/WHEEL +5 -0
- langvio-0.0.1.dist-info/entry_points.txt +2 -0
- langvio-0.0.1.dist-info/licenses/LICENSE +0 -0
- langvio-0.0.1.dist-info/top_level.txt +1 -0
langvio/__init__.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""
|
|
2
|
+
langvio: Connect language models to vision models for natural language visual analysis
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
__version__ = "0.0.1"
|
|
6
|
+
|
|
7
|
+
# === Imports ===
|
|
8
|
+
|
|
9
|
+
# Standard library
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
# Third-party
|
|
13
|
+
import cv2
|
|
14
|
+
import torch
|
|
15
|
+
from dotenv import load_dotenv
|
|
16
|
+
|
|
17
|
+
from langvio.core.pipeline import Pipeline
|
|
18
|
+
|
|
19
|
+
# langvio modules
|
|
20
|
+
from langvio.core.registry import ModelRegistry
|
|
21
|
+
from langvio.llm.base import BaseLLMProcessor
|
|
22
|
+
from langvio.llm.factory import register_llm_processors
|
|
23
|
+
from langvio.vision.base import BaseVisionProcessor
|
|
24
|
+
from langvio.vision.yolo.detector import YOLOProcessor
|
|
25
|
+
|
|
26
|
+
# === Initialization ===
|
|
27
|
+
|
|
28
|
+
# Load environment variables
|
|
29
|
+
load_dotenv()
|
|
30
|
+
|
|
31
|
+
# OpenCV optimizations
|
|
32
|
+
cv2.setNumThreads(4)
|
|
33
|
+
cv2.setUseOptimized(True)
|
|
34
|
+
|
|
35
|
+
# PyTorch optimizations
|
|
36
|
+
if torch.cuda.is_available():
|
|
37
|
+
torch.backends.cudnn.benchmark = True
|
|
38
|
+
|
|
39
|
+
# Initialize model registry
|
|
40
|
+
registry = ModelRegistry()
|
|
41
|
+
|
|
42
|
+
# Register YOLO and YOLOe vision processors
|
|
43
|
+
registry.register_vision_processor(
|
|
44
|
+
"yolo", YOLOProcessor, model_path="yolo11n.pt", confidence=0.5
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
registry.register_vision_processor(
|
|
48
|
+
"yoloe",
|
|
49
|
+
YOLOProcessor,
|
|
50
|
+
model_path="yoloe-11s-seg-pf.pt",
|
|
51
|
+
confidence=0.5,
|
|
52
|
+
model_type="yoloe",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
registry.register_vision_processor(
|
|
56
|
+
"yoloe_medium",
|
|
57
|
+
YOLOProcessor,
|
|
58
|
+
model_path="yoloe-11m-seg-pf.pt",
|
|
59
|
+
confidence=0.5,
|
|
60
|
+
model_type="yoloe",
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
registry.register_vision_processor(
|
|
64
|
+
"yoloe_large",
|
|
65
|
+
YOLOProcessor,
|
|
66
|
+
model_path="yoloe-11l-seg-pf.pt",
|
|
67
|
+
confidence=0.5,
|
|
68
|
+
model_type="yoloe",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Register LLM processors
|
|
72
|
+
register_llm_processors(registry)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# === Pipeline Creator ===
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def create_pipeline(config_path=None, llm_name=None, vision_name=None):
|
|
79
|
+
"""
|
|
80
|
+
Create a pipeline with optional configuration.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
config_path: Path to a configuration file
|
|
84
|
+
llm_name: Name of LLM processor to use
|
|
85
|
+
vision_name: Name of vision processor to use (default: "yoloe")
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
A configured Pipeline instance
|
|
89
|
+
"""
|
|
90
|
+
pipeline = Pipeline(config_path)
|
|
91
|
+
|
|
92
|
+
if vision_name:
|
|
93
|
+
pipeline.set_vision_processor(vision_name)
|
|
94
|
+
else:
|
|
95
|
+
try:
|
|
96
|
+
pipeline.set_vision_processor("yoloe_large")
|
|
97
|
+
except Exception:
|
|
98
|
+
pipeline.set_vision_processor("yolo")
|
|
99
|
+
|
|
100
|
+
if llm_name:
|
|
101
|
+
pipeline.set_llm_processor(llm_name)
|
|
102
|
+
else:
|
|
103
|
+
try:
|
|
104
|
+
default_llm = pipeline.config.config["llm"]["default"]
|
|
105
|
+
pipeline.set_llm_processor(default_llm)
|
|
106
|
+
except Exception:
|
|
107
|
+
if len(registry.list_llm_processors()) == 0:
|
|
108
|
+
error_msg = (
|
|
109
|
+
"ERROR: No LLM providers are installed. "
|
|
110
|
+
"Please install at least one provider:\n"
|
|
111
|
+
"- For OpenAI: pip install langvio[openai]\n"
|
|
112
|
+
"- For Google Gemini: pip install langvio[google]\n"
|
|
113
|
+
"- For all providers: pip install langvio[all-llm]"
|
|
114
|
+
)
|
|
115
|
+
print(error_msg, file=sys.stderr)
|
|
116
|
+
sys.exit(1)
|
|
117
|
+
else:
|
|
118
|
+
available_llm = next(iter(registry.list_llm_processors()))
|
|
119
|
+
pipeline.set_llm_processor(available_llm)
|
|
120
|
+
|
|
121
|
+
return pipeline
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
# === Public Exports ===
|
|
125
|
+
|
|
126
|
+
__all__ = [
|
|
127
|
+
"Pipeline",
|
|
128
|
+
"create_pipeline",
|
|
129
|
+
"registry",
|
|
130
|
+
"BaseLLMProcessor",
|
|
131
|
+
"BaseVisionProcessor",
|
|
132
|
+
]
|
langvio/cli.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command-line interface for langvio
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
from langvio import create_pipeline
|
|
11
|
+
from langvio.utils.file_utils import is_image_file, is_video_file
|
|
12
|
+
from langvio.utils.logging import setup_logging
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def main():
|
|
16
|
+
"""Main entry point for the langvio CLI"""
|
|
17
|
+
# Parse arguments
|
|
18
|
+
parser = argparse.ArgumentParser(
|
|
19
|
+
description="langvio: Connect LLMs with video models"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
# Required arguments
|
|
23
|
+
parser.add_argument("--query", "-q", required=True, help="Natural language query")
|
|
24
|
+
parser.add_argument(
|
|
25
|
+
"--media", "-m", required=True, help="Path to image or video file"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# Optional arguments
|
|
29
|
+
parser.add_argument("--config", "-c", help="Path to configuration file")
|
|
30
|
+
parser.add_argument("--llm", "-l", help="LLM processor to use")
|
|
31
|
+
parser.add_argument("--vision", "-v", help="Vision processor to use")
|
|
32
|
+
parser.add_argument("--output", "-o", help="Output directory for processed media")
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
"--log-level",
|
|
35
|
+
choices=["DEBUG", "INFO", "WARNING", "ERROR"],
|
|
36
|
+
default="INFO",
|
|
37
|
+
help="Logging level",
|
|
38
|
+
)
|
|
39
|
+
parser.add_argument("--log-file", help="Path to log file")
|
|
40
|
+
parser.add_argument(
|
|
41
|
+
"--list-models", action="store_true", help="List available models and exit"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
args = parser.parse_args()
|
|
45
|
+
|
|
46
|
+
# Set up logging
|
|
47
|
+
setup_logging({"level": args.log_level, "file": args.log_file})
|
|
48
|
+
|
|
49
|
+
logger = logging.getLogger(__name__)
|
|
50
|
+
|
|
51
|
+
# List models if requested
|
|
52
|
+
if args.list_models:
|
|
53
|
+
list_available_models()
|
|
54
|
+
return 0
|
|
55
|
+
|
|
56
|
+
# Check if media file exists
|
|
57
|
+
if not os.path.exists(args.media):
|
|
58
|
+
logger.error("Media file not found: %s", args.media)
|
|
59
|
+
return 1
|
|
60
|
+
|
|
61
|
+
# Check if media file is supported
|
|
62
|
+
if not is_image_file(args.media) and not is_video_file(args.media):
|
|
63
|
+
logger.error("Unsupported media file format: %s", args.media)
|
|
64
|
+
return 1
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
# Create pipeline
|
|
68
|
+
pipeline = create_pipeline(
|
|
69
|
+
config_path=args.config, llm_name=args.llm, vision_name=args.vision
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Update output directory if specified
|
|
73
|
+
if args.output:
|
|
74
|
+
pipeline.config.config["media"]["output_dir"] = args.output
|
|
75
|
+
os.makedirs(args.output, exist_ok=True)
|
|
76
|
+
|
|
77
|
+
# Process query
|
|
78
|
+
result = pipeline.process(args.query, args.media)
|
|
79
|
+
|
|
80
|
+
# Print results
|
|
81
|
+
print("\n===== langvio Results =====")
|
|
82
|
+
print("Query: {}".format(result["query"]))
|
|
83
|
+
print("Media: {} ({})".format(result["media_path"], result["media_type"]))
|
|
84
|
+
print("Output: {}".format(result["output_path"]))
|
|
85
|
+
print("\nExplanation: {}".format(result["explanation"]))
|
|
86
|
+
print("\nDetection summary:")
|
|
87
|
+
|
|
88
|
+
# Count detections by label
|
|
89
|
+
counts = {}
|
|
90
|
+
for frame_dets in result["detections"].values():
|
|
91
|
+
for det in frame_dets:
|
|
92
|
+
label = det["label"]
|
|
93
|
+
counts[label] = counts.get(label, 0) + 1
|
|
94
|
+
|
|
95
|
+
for label, count in counts.items():
|
|
96
|
+
print("- {}: {}".format(label, count))
|
|
97
|
+
|
|
98
|
+
print("\nProcessing complete!")
|
|
99
|
+
return 0
|
|
100
|
+
|
|
101
|
+
except Exception as e:
|
|
102
|
+
logger.error("Error processing query: %s", e, exc_info=True)
|
|
103
|
+
return 1
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def list_available_models():
|
|
107
|
+
"""List all available models in the registry"""
|
|
108
|
+
from langvio import registry
|
|
109
|
+
|
|
110
|
+
print("\n===== Available LLM Processors =====")
|
|
111
|
+
for name, cls in registry.list_llm_processors().items():
|
|
112
|
+
print("- {}".format(name))
|
|
113
|
+
|
|
114
|
+
print("\n===== Available Vision Processors =====")
|
|
115
|
+
for name, cls in registry.list_vision_processors().items():
|
|
116
|
+
print("- {}".format(name))
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
if __name__ == "__main__":
|
|
120
|
+
sys.exit(main())
|
langvio/config.py
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration management for langvio
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from typing import Any, Dict, Optional
|
|
7
|
+
|
|
8
|
+
import yaml
|
|
9
|
+
from dotenv import load_dotenv
|
|
10
|
+
|
|
11
|
+
load_dotenv() # Load environment variables from .env
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Config:
|
|
15
|
+
"""Configuration manager for langvio"""
|
|
16
|
+
|
|
17
|
+
DEFAULT_CONFIG_PATH = os.path.join(os.path.dirname(__file__), "default_config.yaml")
|
|
18
|
+
|
|
19
|
+
def __init__(self, config_path: Optional[str] = None):
|
|
20
|
+
"""
|
|
21
|
+
Initialize configuration.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
config_path: Path to a YAML configuration file
|
|
25
|
+
"""
|
|
26
|
+
# Initialize empty config
|
|
27
|
+
self.config = {}
|
|
28
|
+
|
|
29
|
+
# First load default config
|
|
30
|
+
self._load_default_config()
|
|
31
|
+
|
|
32
|
+
# Then load user config if provided
|
|
33
|
+
if config_path and os.path.exists(config_path):
|
|
34
|
+
self.load_config(config_path)
|
|
35
|
+
|
|
36
|
+
def _load_default_config(self) -> None:
|
|
37
|
+
"""Load the default configuration from default_config.yaml"""
|
|
38
|
+
try:
|
|
39
|
+
if os.path.exists(self.DEFAULT_CONFIG_PATH):
|
|
40
|
+
with open(self.DEFAULT_CONFIG_PATH, "r") as f:
|
|
41
|
+
self.config = yaml.safe_load(f)
|
|
42
|
+
if self.config is None: # Handle empty file case
|
|
43
|
+
self.config = {}
|
|
44
|
+
else:
|
|
45
|
+
# Fallback default configuration if file doesn't exist
|
|
46
|
+
self.config = {
|
|
47
|
+
"llm": {
|
|
48
|
+
"default": "gemini",
|
|
49
|
+
"models": {
|
|
50
|
+
"gemini": {
|
|
51
|
+
"model_name": "gemini-pro",
|
|
52
|
+
"model_kwargs": {"temperature": 0.2},
|
|
53
|
+
},
|
|
54
|
+
"gpt": {
|
|
55
|
+
"model_name": "gpt-3.5-turbo",
|
|
56
|
+
"model_kwargs": {"temperature": 0.0},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
"vision": {
|
|
61
|
+
"default": "yolo",
|
|
62
|
+
"models": {
|
|
63
|
+
"yolo": {
|
|
64
|
+
"type": "yolo",
|
|
65
|
+
"model_path": "yolov11n.pt",
|
|
66
|
+
"confidence": 0.25,
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
"media": {
|
|
71
|
+
"output_dir": "./output",
|
|
72
|
+
"temp_dir": "./temp",
|
|
73
|
+
"visualization": {
|
|
74
|
+
"box_color": [0, 255, 0],
|
|
75
|
+
"text_color": [255, 255, 255],
|
|
76
|
+
"line_thickness": 2,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
"logging": {"level": "INFO", "file": None},
|
|
80
|
+
}
|
|
81
|
+
except Exception as e:
|
|
82
|
+
raise ValueError(f"Error loading default configuration: {e}")
|
|
83
|
+
|
|
84
|
+
def load_config(self, config_path: str) -> None:
|
|
85
|
+
"""
|
|
86
|
+
Load configuration from a YAML file.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
config_path: Path to a YAML configuration file
|
|
90
|
+
"""
|
|
91
|
+
try:
|
|
92
|
+
with open(config_path, "r") as f:
|
|
93
|
+
user_config = yaml.safe_load(f)
|
|
94
|
+
if user_config is None: # Handle empty file case
|
|
95
|
+
return
|
|
96
|
+
|
|
97
|
+
# Update configuration
|
|
98
|
+
self._update_config(self.config, user_config)
|
|
99
|
+
except Exception as e:
|
|
100
|
+
raise ValueError(f"Error loading configuration from {config_path}: {e}")
|
|
101
|
+
|
|
102
|
+
def _update_config(
|
|
103
|
+
self, base_config: Dict[str, Any], new_config: Dict[str, Any]
|
|
104
|
+
) -> None:
|
|
105
|
+
"""Recursively update base config with new config."""
|
|
106
|
+
for key, value in new_config.items():
|
|
107
|
+
if (
|
|
108
|
+
isinstance(value, dict)
|
|
109
|
+
and key in base_config
|
|
110
|
+
and isinstance(base_config[key], dict)
|
|
111
|
+
):
|
|
112
|
+
self._update_config(base_config[key], value)
|
|
113
|
+
else:
|
|
114
|
+
base_config[key] = value
|
|
115
|
+
|
|
116
|
+
def get_llm_config(self, model_name: Optional[str] = None) -> Dict[str, Any]:
|
|
117
|
+
"""
|
|
118
|
+
Get configuration for an LLM model.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
model_name: Name of the model to get config for
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
Model configuration dictionary
|
|
125
|
+
"""
|
|
126
|
+
if not model_name:
|
|
127
|
+
model_name = self.config["llm"]["default"]
|
|
128
|
+
|
|
129
|
+
if model_name not in self.config["llm"]["models"]:
|
|
130
|
+
raise ValueError(f"LLM model '{model_name}' not found in configuration")
|
|
131
|
+
|
|
132
|
+
return self.config["llm"]["models"][model_name]
|
|
133
|
+
|
|
134
|
+
def get_vision_config(self, model_name: Optional[str] = None) -> Dict[str, Any]:
|
|
135
|
+
"""
|
|
136
|
+
Get configuration for a vision model.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
model_name: Name of the model to get config for
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Model configuration dictionary
|
|
143
|
+
"""
|
|
144
|
+
if not model_name:
|
|
145
|
+
model_name = self.config["vision"]["default"]
|
|
146
|
+
|
|
147
|
+
if model_name not in self.config["vision"]["models"]:
|
|
148
|
+
raise ValueError(f"Vision model '{model_name}' not found in configuration")
|
|
149
|
+
|
|
150
|
+
return self.config["vision"]["models"][model_name]
|
|
151
|
+
|
|
152
|
+
def get_media_config(self) -> Dict[str, Any]:
|
|
153
|
+
"""
|
|
154
|
+
Get media processing configuration.
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
Media configuration dictionary
|
|
158
|
+
"""
|
|
159
|
+
return self.config["media"]
|
|
160
|
+
|
|
161
|
+
def get_logging_config(self) -> Dict[str, Any]:
|
|
162
|
+
"""
|
|
163
|
+
Get logging configuration.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
Logging configuration dictionary
|
|
167
|
+
"""
|
|
168
|
+
return self.config["logging"]
|
|
169
|
+
|
|
170
|
+
def get_langsmith_config(self) -> Dict[str, Any]:
|
|
171
|
+
"""
|
|
172
|
+
Get LangSmith configuration if available.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
LangSmith configuration dictionary
|
|
176
|
+
"""
|
|
177
|
+
return self.config.get("langsmith", {})
|
|
178
|
+
|
|
179
|
+
def save_config(self, config_path: str) -> None:
|
|
180
|
+
"""
|
|
181
|
+
Save current configuration to a YAML file.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
config_path: Path to save the configuration
|
|
185
|
+
"""
|
|
186
|
+
try:
|
|
187
|
+
with open(config_path, "w") as f:
|
|
188
|
+
yaml.dump(self.config, f, default_flow_style=False)
|
|
189
|
+
except Exception as e:
|
|
190
|
+
raise ValueError(f"Error saving configuration to {config_path}: {e}")
|
langvio/core/__init__.py
ADDED
|
File without changes
|
langvio/core/base.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base classes for langvio components
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from typing import Any, Dict, Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Processor(ABC):
|
|
10
|
+
"""Base class for all processors in langvio"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, name: str, config: Optional[Dict[str, Any]] = None):
|
|
13
|
+
"""
|
|
14
|
+
Initialize processor.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
name: Processor name
|
|
18
|
+
config: Configuration parameters
|
|
19
|
+
"""
|
|
20
|
+
self.name = name
|
|
21
|
+
self.config = config or {}
|
|
22
|
+
|
|
23
|
+
@abstractmethod
|
|
24
|
+
def initialize(self) -> bool:
|
|
25
|
+
"""
|
|
26
|
+
Initialize the processor with its configuration.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
True if initialization was successful
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def from_config(cls, name: str, config: Dict[str, Any]) -> "Processor":
|
|
34
|
+
"""
|
|
35
|
+
Create a processor from configuration.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
name: Processor name
|
|
39
|
+
config: Configuration parameters
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Initialized processor
|
|
43
|
+
"""
|
|
44
|
+
processor = cls(name, config)
|
|
45
|
+
processor.initialize()
|
|
46
|
+
return processor
|
langvio/core/pipeline.py
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Enhanced core pipeline for connecting LLMs with vision models - refactored
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
from typing import Any, Dict, Optional
|
|
8
|
+
|
|
9
|
+
from langvio.config import Config
|
|
10
|
+
from langvio.core.processor_manager import ProcessorManager
|
|
11
|
+
from langvio.core.visualization_manager import VisualizationManager
|
|
12
|
+
from langvio.utils.file_utils import is_video_file
|
|
13
|
+
from langvio.utils.logging import setup_logging
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Pipeline:
|
|
17
|
+
"""Main pipeline for processing queries with LLMs and vision models"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, config_path: Optional[str] = None):
|
|
20
|
+
"""Initialize pipeline"""
|
|
21
|
+
# Initialize configuration
|
|
22
|
+
self.config = Config(config_path)
|
|
23
|
+
|
|
24
|
+
# Set up logging
|
|
25
|
+
setup_logging(self.config.get_logging_config())
|
|
26
|
+
self.logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
# Initialize managers
|
|
29
|
+
self.processor_manager = ProcessorManager(self.config)
|
|
30
|
+
self.visualization_manager = VisualizationManager(self.config)
|
|
31
|
+
|
|
32
|
+
self.logger.info("Enhanced Pipeline initialized")
|
|
33
|
+
|
|
34
|
+
def load_config(self, config_path: str) -> None:
|
|
35
|
+
"""Load configuration from file"""
|
|
36
|
+
self.config.load_config(config_path)
|
|
37
|
+
self.logger.info(f"Loaded configuration from {config_path}")
|
|
38
|
+
|
|
39
|
+
# Update managers with new config
|
|
40
|
+
self.processor_manager.config = self.config
|
|
41
|
+
self.visualization_manager.config = self.config
|
|
42
|
+
|
|
43
|
+
def set_llm_processor(self, processor_name: str) -> None:
|
|
44
|
+
"""Set the LLM processor"""
|
|
45
|
+
self.processor_manager.set_llm_processor(processor_name)
|
|
46
|
+
|
|
47
|
+
def set_vision_processor(self, processor_name: str) -> None:
|
|
48
|
+
"""Set the vision processor"""
|
|
49
|
+
self.processor_manager.set_vision_processor(processor_name)
|
|
50
|
+
|
|
51
|
+
def process(self, query: str, media_path: str) -> Dict[str, Any]:
|
|
52
|
+
"""Process a query on media with enhanced capabilities"""
|
|
53
|
+
self.logger.info(f"Processing query: {query}")
|
|
54
|
+
|
|
55
|
+
# Check if processors are set
|
|
56
|
+
if not self.processor_manager.has_processors():
|
|
57
|
+
if not self.processor_manager.llm_processor:
|
|
58
|
+
error_msg = "ERROR: LLM processor not set"
|
|
59
|
+
self.logger.error(error_msg)
|
|
60
|
+
raise ValueError(error_msg)
|
|
61
|
+
|
|
62
|
+
if not self.processor_manager.vision_processor:
|
|
63
|
+
error_msg = "ERROR: Vision processor not set"
|
|
64
|
+
self.logger.error(error_msg)
|
|
65
|
+
raise ValueError(error_msg)
|
|
66
|
+
|
|
67
|
+
# Check if media file exists
|
|
68
|
+
if not os.path.exists(media_path):
|
|
69
|
+
error_msg = f"ERROR: Media file not found: {media_path}"
|
|
70
|
+
self.logger.error(error_msg)
|
|
71
|
+
raise FileNotFoundError(error_msg)
|
|
72
|
+
|
|
73
|
+
# Check media type
|
|
74
|
+
is_video = is_video_file(media_path)
|
|
75
|
+
media_type = "video" if is_video else "image"
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
# 1. Parse query with LLM to get structured parameters
|
|
79
|
+
query_params = self.processor_manager.parse_query(query)
|
|
80
|
+
self.logger.info(f"Parsed query params: {query_params}")
|
|
81
|
+
|
|
82
|
+
# 2. Run detection with vision processor
|
|
83
|
+
all_detections = self.processor_manager.process_media(
|
|
84
|
+
media_path, query_params
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Log detection results to JSON
|
|
88
|
+
self._log_detections_to_json(all_detections, query, media_path)
|
|
89
|
+
|
|
90
|
+
# 3. Generate explanation using all detected objects and metrics
|
|
91
|
+
explanation = self.processor_manager.generate_explanation(
|
|
92
|
+
query, all_detections
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# 4. Get highlighted objects from the LLM processor
|
|
96
|
+
highlighted_objects = self.processor_manager.get_highlighted_objects()
|
|
97
|
+
|
|
98
|
+
# 5. Create visualization with highlighted objects
|
|
99
|
+
output_path = self.visualization_manager.create_visualization(
|
|
100
|
+
media_path, all_detections, highlighted_objects, query_params
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Prepare result
|
|
104
|
+
result = {
|
|
105
|
+
"query": query,
|
|
106
|
+
"media_path": media_path,
|
|
107
|
+
"media_type": media_type,
|
|
108
|
+
"output_path": output_path,
|
|
109
|
+
"explanation": explanation,
|
|
110
|
+
"detections": all_detections,
|
|
111
|
+
"query_params": query_params,
|
|
112
|
+
"highlighted_objects": highlighted_objects,
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
self.logger.info("Processed query successfully")
|
|
116
|
+
return result
|
|
117
|
+
|
|
118
|
+
except Exception as e:
|
|
119
|
+
self.logger.error(f"Error processing query: {e}")
|
|
120
|
+
raise
|
|
121
|
+
|
|
122
|
+
def _log_detections_to_json(
|
|
123
|
+
self, all_detections: Dict[str, Any], query: str, media_path: str
|
|
124
|
+
):
|
|
125
|
+
"""Log detection results to JSON file"""
|
|
126
|
+
import json
|
|
127
|
+
import os
|
|
128
|
+
from datetime import datetime
|
|
129
|
+
|
|
130
|
+
# Create logs directory if it doesn't exist
|
|
131
|
+
logs_dir = "logs"
|
|
132
|
+
os.makedirs(logs_dir, exist_ok=True)
|
|
133
|
+
|
|
134
|
+
# Create filename with timestamp
|
|
135
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
136
|
+
media_name = os.path.splitext(os.path.basename(media_path))[0]
|
|
137
|
+
filename = f"detections_{media_name}_{timestamp}.json"
|
|
138
|
+
filepath = os.path.join(logs_dir, filename)
|
|
139
|
+
|
|
140
|
+
# Prepare detection log data
|
|
141
|
+
detection_log = {
|
|
142
|
+
"query": query,
|
|
143
|
+
"media_path": media_path,
|
|
144
|
+
"timestamp": datetime.now().isoformat(),
|
|
145
|
+
"detections": all_detections,
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
# Write to JSON file
|
|
149
|
+
try:
|
|
150
|
+
with open(filepath, "w", encoding="utf-8") as f:
|
|
151
|
+
json.dump(detection_log, f, indent=2, ensure_ascii=False)
|
|
152
|
+
self.logger.info(f"Detection results logged to: {filepath}")
|
|
153
|
+
except Exception as e:
|
|
154
|
+
self.logger.error(f"Failed to log detection results to file: {e}")
|