Spaces:
Sleeping
Sleeping
Upload research_synthesis.py
Browse files
metadata_search/research_synthesis.py
CHANGED
|
@@ -36,6 +36,7 @@ does NOT patch module globals, and therefore does not change production calls.
|
|
| 36 |
from __future__ import annotations
|
| 37 |
|
| 38 |
import math
|
|
|
|
| 39 |
import os
|
| 40 |
import time
|
| 41 |
import types
|
|
@@ -283,7 +284,7 @@ def register_research_synthesis_route(research_bp):
|
|
| 283 |
expected_token = os.getenv("WATERSCOPE_RESEARCH_TOKEN", "").strip()
|
| 284 |
if expected_token:
|
| 285 |
supplied = request.headers.get("X-Research-Token", "").strip()
|
| 286 |
-
if supplied
|
| 287 |
return jsonify({"ok": False, "error": "Unauthorized"}), 401
|
| 288 |
|
| 289 |
body = request.get_json(force=True, silent=True) or {}
|
|
@@ -337,6 +338,29 @@ def register_research_synthesis_route(research_bp):
|
|
| 337 |
application = _clean(body.get("application")) or "auto"
|
| 338 |
model = _clean(body.get("model")) or None
|
| 339 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 340 |
try:
|
| 341 |
max_words_per_paper = int(
|
| 342 |
body.get(
|
|
@@ -443,15 +467,40 @@ def register_research_synthesis_route(research_bp):
|
|
| 443 |
)
|
| 444 |
|
| 445 |
model_start = time.perf_counter()
|
| 446 |
-
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
|
|
|
|
|
|
|
|
|
| 450 |
application=application,
|
| 451 |
-
|
| 452 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 453 |
)
|
| 454 |
-
)
|
| 455 |
model_call_elapsed_s = (
|
| 456 |
time.perf_counter() - model_start
|
| 457 |
)
|
|
@@ -474,6 +523,9 @@ def register_research_synthesis_route(research_bp):
|
|
| 474 |
answer_words > max_output_words
|
| 475 |
),
|
| 476 |
"strategy": strategy,
|
|
|
|
|
|
|
|
|
|
| 477 |
"application_requested": application,
|
| 478 |
"application_used": application_used,
|
| 479 |
"model_requested": model or "",
|
|
@@ -526,7 +578,7 @@ def register_research_synthesis_route(research_bp):
|
|
| 526 |
"mcp" if chat_api.use_mcp() else "direct"
|
| 527 |
),
|
| 528 |
"diagnostics": diagnostics,
|
| 529 |
-
"llm_backend": chat_api.llm_backend_name(),
|
| 530 |
"trace_status": {
|
| 531 |
"captured_full_context": bool(
|
| 532 |
full_context
|
|
@@ -555,7 +607,7 @@ def register_research_synthesis_route(research_bp):
|
|
| 555 |
MAX_RESEARCH_PAPERS
|
| 556 |
),
|
| 557 |
"token_override_scope": (
|
| 558 |
-
"isolated_function_clone"
|
| 559 |
),
|
| 560 |
},
|
| 561 |
}
|
|
|
|
| 36 |
from __future__ import annotations
|
| 37 |
|
| 38 |
import math
|
| 39 |
+
import hmac
|
| 40 |
import os
|
| 41 |
import time
|
| 42 |
import types
|
|
|
|
| 284 |
expected_token = os.getenv("WATERSCOPE_RESEARCH_TOKEN", "").strip()
|
| 285 |
if expected_token:
|
| 286 |
supplied = request.headers.get("X-Research-Token", "").strip()
|
| 287 |
+
if not hmac.compare_digest(supplied, expected_token):
|
| 288 |
return jsonify({"ok": False, "error": "Unauthorized"}), 401
|
| 289 |
|
| 290 |
body = request.get_json(force=True, silent=True) or {}
|
|
|
|
| 338 |
application = _clean(body.get("application")) or "auto"
|
| 339 |
model = _clean(body.get("model")) or None
|
| 340 |
|
| 341 |
+
# Optional research-only provider selection. Existing HF requests
|
| 342 |
+
# remain the default and keep their original auto-routing behavior.
|
| 343 |
+
provider = (_clean(body.get("provider")) or "hf").lower()
|
| 344 |
+
if provider not in {"hf", "azure"}:
|
| 345 |
+
return jsonify({"ok": False, "error": "provider must be hf or azure"}), 400
|
| 346 |
+
if provider == "azure":
|
| 347 |
+
if os.getenv("ENABLE_EXTERNAL_LLM", "false").lower() not in {"1", "true", "yes"}:
|
| 348 |
+
return jsonify({"ok": False, "error": "External LLM is disabled"}), 403
|
| 349 |
+
# A research-only secret is mandatory for paid Azure calls.
|
| 350 |
+
research_token = os.getenv("WATERSCOPE_RESEARCH_TOKEN", "").strip()
|
| 351 |
+
supplied_token = request.headers.get("X-Research-Token", "").strip()
|
| 352 |
+
if not research_token or not hmac.compare_digest(supplied_token, research_token):
|
| 353 |
+
return jsonify({"ok": False, "error": "Unauthorized"}), 401
|
| 354 |
+
# The HF 'auto' application detection lives in the model Space;
|
| 355 |
+
# external calls require explicit matching application prompts.
|
| 356 |
+
from llm_services.agent_prompts import AGENT_APPLICATIONS
|
| 357 |
+
if application not in AGENT_APPLICATIONS:
|
| 358 |
+
return jsonify({
|
| 359 |
+
"ok": False,
|
| 360 |
+
"error": "For Azure, specify an application: "
|
| 361 |
+
+ ", ".join(AGENT_APPLICATIONS),
|
| 362 |
+
}), 400
|
| 363 |
+
|
| 364 |
try:
|
| 365 |
max_words_per_paper = int(
|
| 366 |
body.get(
|
|
|
|
| 467 |
)
|
| 468 |
|
| 469 |
model_start = time.perf_counter()
|
| 470 |
+
external_result = None
|
| 471 |
+
if provider == "azure":
|
| 472 |
+
from llm_services.llm_client import run_application
|
| 473 |
+
# The same complete context, question and application prompt
|
| 474 |
+
# are passed to both model paths. HF's prefix_context() is
|
| 475 |
+
# preserved in the external prompt as well.
|
| 476 |
+
external_result = run_application(
|
| 477 |
application=application,
|
| 478 |
+
context=model_context,
|
| 479 |
+
question=question_to_model,
|
| 480 |
+
provider="azure",
|
| 481 |
+
model=None, # use AZURE_LLM_DEPLOYMENT, not HF selector
|
| 482 |
+
max_completion_tokens=max_new_tokens_requested,
|
| 483 |
+
)
|
| 484 |
+
answer = external_result["answer"]
|
| 485 |
+
strategy = f"waterscope:{application}"
|
| 486 |
+
diagnostics = {
|
| 487 |
+
"provider": "azure",
|
| 488 |
+
"model": external_result.get("model_returned"),
|
| 489 |
+
"finish_reason": external_result.get("finish_reason"),
|
| 490 |
+
"metering": external_result.get("metering"),
|
| 491 |
+
"prompt_version": external_result.get("prompt_version"),
|
| 492 |
+
"prompt_sha256": external_result.get("prompt_sha256"),
|
| 493 |
+
}
|
| 494 |
+
else:
|
| 495 |
+
answer, strategy, diagnostics = (
|
| 496 |
+
_research_generate_answer(
|
| 497 |
+
question_to_model,
|
| 498 |
+
full_context,
|
| 499 |
+
application=application,
|
| 500 |
+
model=model,
|
| 501 |
+
max_new_tokens=max_new_tokens_requested,
|
| 502 |
+
)
|
| 503 |
)
|
|
|
|
| 504 |
model_call_elapsed_s = (
|
| 505 |
time.perf_counter() - model_start
|
| 506 |
)
|
|
|
|
| 523 |
answer_words > max_output_words
|
| 524 |
),
|
| 525 |
"strategy": strategy,
|
| 526 |
+
"provider_used": provider,
|
| 527 |
+
"external_model": external_result.get("model_returned") if external_result else None,
|
| 528 |
+
"external_usage": external_result.get("metering") if external_result else None,
|
| 529 |
"application_requested": application,
|
| 530 |
"application_used": application_used,
|
| 531 |
"model_requested": model or "",
|
|
|
|
| 578 |
"mcp" if chat_api.use_mcp() else "direct"
|
| 579 |
),
|
| 580 |
"diagnostics": diagnostics,
|
| 581 |
+
"llm_backend": "azure" if provider == "azure" else chat_api.llm_backend_name(),
|
| 582 |
"trace_status": {
|
| 583 |
"captured_full_context": bool(
|
| 584 |
full_context
|
|
|
|
| 607 |
MAX_RESEARCH_PAPERS
|
| 608 |
),
|
| 609 |
"token_override_scope": (
|
| 610 |
+
"external_api" if provider == "azure" else "isolated_function_clone"
|
| 611 |
),
|
| 612 |
},
|
| 613 |
}
|