Spaces:
Sleeping
Sleeping
| import json | |
| import os | |
| from dataclasses import dataclass | |
| from pathlib import Path | |
| from typing import Optional | |
| import diskcache | |
| import openai | |
| from dotenv import load_dotenv | |
| from inference_utils import sanitize_path_component | |
| from llm_query.span_marking.utils import is_pass_response | |
| load_dotenv() | |
| CONFIGS_DIR = Path(__file__).parent / "configs" | |
| class SpanMarkingLLMResponse: | |
| model: str | |
| config: str | |
| text_a: str | |
| text_b: str | |
| marked_text_a: Optional[str] | |
| response_obj: dict | |
| class AzureClient: | |
| API_KEY_NAME = "AZURE_API_KEY" | |
| def __init__( | |
| self, | |
| model_name: str = "gpt-5.6-terra", | |
| config_name: str = "config.span_marking.v0.6.json", | |
| cache_directory: Optional[Path] = None, | |
| use_cache: bool = True, | |
| ): | |
| self.config_name = config_name | |
| with open(CONFIGS_DIR / config_name) as config_file: | |
| self.config = json.load(config_file) | |
| assert "{text_a}" in self.config["prompt_template"] | |
| assert "{text_b}" in self.config["prompt_template"] | |
| for example in self.config.get("examples", []): | |
| assert "text_a" in example | |
| assert "text_b" in example | |
| assert "marked_text_a" in example | |
| self.model_name = model_name | |
| self.BASE_URL = os.environ.get("AZURE_BASE_URL") | |
| self.cache_directory = cache_directory or Path(__file__).parent / ".llm_cache" | |
| self.client = openai.Client( | |
| api_key=os.environ.get(self.API_KEY_NAME), | |
| base_url=self.BASE_URL, | |
| ) | |
| self.cache = None | |
| if use_cache: | |
| self.cache = diskcache.Cache(self.model_cache_dir) | |
| def model_cache_dir(self) -> Path: | |
| sanitized_model_name = sanitize_path_component(self.model_name) | |
| config_stem = sanitize_path_component(self.config_name.removesuffix(".json")) | |
| return self.cache_directory / f"{sanitized_model_name}_{config_stem}" | |
| def _get_cache_key(self, text_a: str, text_b: str) -> tuple[str, str, str, str]: | |
| return (self.model_name, self.config_name, text_a, text_b) | |
| def _completion_extra_kwargs(self) -> dict: | |
| kwargs = {"reasoning_effort": "none"} | |
| for key in ("seed", "temperature"): | |
| if key in self.config: | |
| kwargs[key] = self.config[key] | |
| return kwargs | |
| def _format_prompt(self, text_a: str, text_b: str) -> str: | |
| return ( | |
| self.config["prompt_template"] | |
| .replace("{text_a}", text_a) | |
| .replace("{text_b}", text_b) | |
| ) | |
| def _build_messages(self, text_a: str, text_b: str) -> list[dict]: | |
| messages = [] | |
| if self.config.get("use_examples", False): | |
| for example in self.config.get("examples", []): | |
| messages.append({ | |
| "role": "user", | |
| "content": self._format_prompt(example["text_a"], example["text_b"]), | |
| }) | |
| messages.append({ | |
| "role": "assistant", | |
| "content": example["marked_text_a"], | |
| }) | |
| messages.append({ | |
| "role": "user", | |
| "content": self._format_prompt(text_a, text_b), | |
| }) | |
| return messages | |
| def query(self, text_a: str, text_b: str) -> SpanMarkingLLMResponse: | |
| cache_key = self._get_cache_key(text_a, text_b) | |
| if self.cache is not None and cache_key in self.cache: | |
| return self._build_response(text_a, text_b, self.cache[cache_key]) | |
| completion = self.client.chat.completions.create( | |
| model=self.model_name, | |
| messages=self._build_messages(text_a, text_b), | |
| **self._completion_extra_kwargs(), | |
| ) | |
| response_obj = completion.to_dict() | |
| if self.cache is not None: | |
| self.cache[cache_key] = response_obj | |
| return self._build_response(text_a, text_b, response_obj) | |
| def _normalize_marked_text(content: str, text_a: str) -> str: | |
| stripped = content.strip() | |
| if is_pass_response(stripped): | |
| return text_a | |
| candidate = stripped | |
| if len(candidate) >= 2 and candidate[0] == candidate[-1] and candidate[0] in "\"'": | |
| candidate = candidate[1:-1].strip() | |
| return candidate | |
| return stripped | |
| def _build_response( | |
| self, | |
| text_a: str, | |
| text_b: str, | |
| response_obj: dict, | |
| ) -> SpanMarkingLLMResponse: | |
| llm_response = SpanMarkingLLMResponse( | |
| model=self.model_name, | |
| config=self.config_name, | |
| text_a=text_a, | |
| text_b=text_b, | |
| marked_text_a=None, | |
| response_obj=response_obj, | |
| ) | |
| try: | |
| content = response_obj["choices"][0]["message"]["content"] | |
| except (KeyError, IndexError, TypeError): | |
| return llm_response | |
| if content is None: | |
| return llm_response | |
| llm_response.marked_text_a = self._normalize_marked_text(content, text_a) | |
| return llm_response | |