starfriend commited on
Commit
ce2592a
·
verified ·
1 Parent(s): 279afe1

Upload 4 files

Browse files
backend/media_analysis/figure_analysis.py CHANGED
@@ -117,7 +117,7 @@ def prepare_media_evidence(
117
 
118
 
119
  def analyze_media_question(question: str, selected_papers: list[dict], *, enabled: bool | None = None,
120
- figures_lookup=None, asset_resolver=None, vision_client=None) -> dict:
121
  """Explicit opt-in entry point. Never silently substitutes a text summary answer."""
122
  if enabled is None:
123
  enabled = os.getenv("WATERSCOPE_ENABLE_MEDIA_ANALYSIS", "0").lower() in ("1", "true", "yes")
@@ -128,5 +128,5 @@ def analyze_media_question(question: str, selected_papers: list[dict], *, enable
128
  if vision_client is None:
129
  from .vision_client import analyze_images
130
  vision_client = analyze_images
131
- result = vision_client(question, items)
132
  return {"status": "ok", "vision_inspected": True, **result}
 
117
 
118
 
119
  def analyze_media_question(question: str, selected_papers: list[dict], *, enabled: bool | None = None,
120
+ figures_lookup=None, asset_resolver=None, vision_client=None, provider: str = "azure", model: str | None = None) -> dict:
121
  """Explicit opt-in entry point. Never silently substitutes a text summary answer."""
122
  if enabled is None:
123
  enabled = os.getenv("WATERSCOPE_ENABLE_MEDIA_ANALYSIS", "0").lower() in ("1", "true", "yes")
 
128
  if vision_client is None:
129
  from .vision_client import analyze_images
130
  vision_client = analyze_images
131
+ result = vision_client(question, items, provider=provider, model=model)
132
  return {"status": "ok", "vision_inspected": True, **result}
backend/media_analysis/test_routes.py CHANGED
@@ -33,6 +33,14 @@ def media_analyze_test():
33
  body = request.get_json(silent=True)
34
  if not isinstance(body, dict):
35
  return jsonify({"error": "JSON object required"}), 400
 
 
 
 
 
 
 
 
36
  question = body.get("question")
37
  papers = body.get("selected_papers")
38
  if not isinstance(question, str) or not question.strip() or len(question) > 4000:
@@ -51,7 +59,7 @@ def media_analyze_test():
51
  return jsonify({"error": "Paper summary exceeds limit"}), 400
52
  sanitized.append(clean)
53
  try:
54
- result = analyze_media_question(question, sanitized, enabled=True)
55
  return jsonify(result)
56
  except (MediaSelectionError, VisionConfigurationError, ValueError, FileNotFoundError) as exc:
57
  return jsonify({"error": str(exc), "vision_inspected": False}), 400
 
33
  body = request.get_json(silent=True)
34
  if not isinstance(body, dict):
35
  return jsonify({"error": "JSON object required"}), 400
36
+ provider = str(body.get("provider", "azure")).strip().lower()
37
+ if provider not in ("azure", "deepseek"):
38
+ return jsonify({"error": "provider must be azure or deepseek"}), 400
39
+ model = body.get("model")
40
+ if model is not None:
41
+ if not isinstance(model, str) or not (1 <= len(model.strip()) <= 100):
42
+ return jsonify({"error": "model must be a nonempty string of at most 100 characters"}), 400
43
+ model = model.strip()
44
  question = body.get("question")
45
  papers = body.get("selected_papers")
46
  if not isinstance(question, str) or not question.strip() or len(question) > 4000:
 
59
  return jsonify({"error": "Paper summary exceeds limit"}), 400
60
  sanitized.append(clean)
61
  try:
62
+ result = analyze_media_question(question, sanitized, enabled=True, provider=provider, model=model)
63
  return jsonify(result)
64
  except (MediaSelectionError, VisionConfigurationError, ValueError, FileNotFoundError) as exc:
65
  return jsonify({"error": str(exc), "vision_inspected": False}), 400
backend/media_analysis/vision_client.py CHANGED
@@ -1,4 +1,4 @@
1
- """Opt-in Azure OpenAI Responses API client for scientific publication media.
2
 
3
  Dependencies: openai>=1.99 (a version with responses.create).
4
  Environment: existing AZURE_OPENAI_ENDPOINT, AZURE_LLM_DEPLOYMENT,
@@ -64,6 +64,7 @@ def _image_part(path: Path, detail: str = "high", max_bytes: int = 15_000_000):
64
 
65
 
66
  def analyze_images(question: str, evidence: list[dict], *, model: str | None = None,
 
67
  detail: str = "high", max_output_tokens: int = 1000,
68
  client=None) -> dict:
69
  """Analyze pre-resolved evidence records containing image_path/caption/paper identifiers.
@@ -79,12 +80,25 @@ def analyze_images(question: str, evidence: list[dict], *, model: str | None = N
79
  raise ValueError("POC permits at most four images per request")
80
  if detail not in ("auto", "low", "high"):
81
  raise ValueError("detail must be auto, low or high")
82
- model = model or os.getenv("AZURE_OPENAI_VISION_DEPLOYMENT") or _env_required("AZURE_LLM_DEPLOYMENT")
 
 
 
 
 
 
 
 
83
  if client is None:
84
  from openai import OpenAI
85
- client = OpenAI(base_url=_azure_responses_base_url(),
86
- api_key=_env_required("AZURE_OPENAI_API_KEY"),
87
- timeout=120.0, max_retries=1)
 
 
 
 
 
88
 
89
  instructions = (
90
  "You analyze scientific publication figures and tables. Use only supplied images, "
@@ -122,7 +136,8 @@ def analyze_images(question: str, evidence: list[dict], *, model: str | None = N
122
  usage = getattr(response, "usage", None)
123
  return {
124
  "answer": getattr(response, "output_text", "") or "",
125
- "strategy": "media_analysis:azure_responses",
 
126
  "model": model,
127
  "sources": used,
128
  "usage": {
 
1
+ """Opt-in Azure or DeepSeek Responses API client for scientific publication media.
2
 
3
  Dependencies: openai>=1.99 (a version with responses.create).
4
  Environment: existing AZURE_OPENAI_ENDPOINT, AZURE_LLM_DEPLOYMENT,
 
64
 
65
 
66
  def analyze_images(question: str, evidence: list[dict], *, model: str | None = None,
67
+ provider: str = "azure",
68
  detail: str = "high", max_output_tokens: int = 1000,
69
  client=None) -> dict:
70
  """Analyze pre-resolved evidence records containing image_path/caption/paper identifiers.
 
80
  raise ValueError("POC permits at most four images per request")
81
  if detail not in ("auto", "low", "high"):
82
  raise ValueError("detail must be auto, low or high")
83
+ provider = str(provider).strip().lower()
84
+ if provider not in ("azure", "deepseek"):
85
+ raise ValueError("provider must be azure or deepseek")
86
+ if provider == "azure":
87
+ model = model or os.getenv("AZURE_OPENAI_VISION_DEPLOYMENT") or _env_required("AZURE_LLM_DEPLOYMENT")
88
+ else:
89
+ model = model or "deepseek-flash"
90
+ if model not in {"deepseek-flash", "deepseek-v4-pro"}:
91
+ raise ValueError("DeepSeek model must be deepseek-flash or deepseek-v4-pro")
92
  if client is None:
93
  from openai import OpenAI
94
+ if provider == "azure":
95
+ client = OpenAI(base_url=_azure_responses_base_url(),
96
+ api_key=_env_required("AZURE_OPENAI_API_KEY"),
97
+ timeout=180.0, max_retries=1)
98
+ else:
99
+ client = OpenAI(base_url="https://api.deepseek.com",
100
+ api_key=_env_required("DEEPSEEK_API_KEY"),
101
+ timeout=180.0, max_retries=1)
102
 
103
  instructions = (
104
  "You analyze scientific publication figures and tables. Use only supplied images, "
 
136
  usage = getattr(response, "usage", None)
137
  return {
138
  "answer": getattr(response, "output_text", "") or "",
139
+ "strategy": f"media_analysis:{provider}_responses",
140
+ "provider": provider,
141
  "model": model,
142
  "sources": used,
143
  "usage": {