omgy commited on
Commit
ff67cf9
·
verified ·
1 Parent(s): fccc913

Update gemini_utils.py

Browse files
Files changed (1) hide show
  1. gemini_utils.py +128 -68
gemini_utils.py CHANGED
@@ -1,11 +1,10 @@
1
  import os
2
  import json
3
  import re
4
- from typing import Optional
5
 
6
  from openai import OpenAI
7
 
8
-
9
  # Environment configuration
10
  NVIDIA_API_KEY = os.getenv("NVIDIA_API_KEY")
11
  NIM_BASE_URL = os.getenv("NIM_BASE_URL", "https://integrate.api.nvidia.com/v1")
@@ -13,63 +12,156 @@ NIM_MODEL_DEFAULT = os.getenv("NIM_MODEL", "meta/llama-3.1-8b-instruct")
13
 
14
  # Sanitization utilities
15
  _PREFACE_RE = re.compile(r"^(okay[, ]|sure[, ]|here(?:'|’)s|summary:?|note:?|context:)\b", re.I)
16
- # Customize anchors for your domain if you have reliable headings
17
  _ANCHOR_RE = re.compile(r"\b(meeting\s*minutes|minutes\s*of\s*meeting|invoice|report|summary)\b", re.I)
18
  _DOC_BLOCK_RE = re.compile(r"\[\[\[DOC\]\]\](.*)\[\[\[\/DOC\]\]\]", re.S)
19
 
20
-
21
  def _sanitize_preface(text: str) -> str:
22
- """Remove typical LLM prefaces and trim to a reliable anchor if present."""
23
  s = (text or "").lstrip()
24
-
25
- # Remove obvious preface lines at the start
26
  lines = s.splitlines()
27
  while lines and _PREFACE_RE.match(lines[0].strip()):
28
  lines.pop(0)
29
  s = "\n".join(lines).lstrip()
30
 
31
- # If your documents have a reliable heading/anchor, trim to it
32
  m = _ANCHOR_RE.search(s)
33
  if m:
34
  s = s[m.start():]
35
 
36
  return s.strip()
37
 
38
-
39
  def _extract_marked_block(text: str) -> Optional[str]:
40
- """Keep only [[[DOC]]] ... [[[/DOC]]] if present."""
41
  m = _DOC_BLOCK_RE.search(text or "")
42
  if m:
43
  return m.group(1).strip()
44
  return None
45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
- def enhance_with_nim(
48
  extracted_text: str,
49
  user_prompt: str,
50
- model: Optional[str] = None,
51
  timeout_s: int = 60,
52
  ) -> str:
53
  """
54
- Enhance document using NVIDIA NIM (OpenAI-compatible Chat Completions).
55
- Enforces JSON-only output: {"enhanced_text": "..."}.
56
- Returns only the enhanced text (string). On any failure, returns original text.
 
 
57
  """
58
  if not NVIDIA_API_KEY:
59
- # No key available -> return original text
60
  return extracted_text
61
 
62
- model_name = model or NIM_MODEL_DEFAULT
63
  client = OpenAI(api_key=NVIDIA_API_KEY, base_url=NIM_BASE_URL)
64
 
65
  system = (
66
  "You are a professional document editor. Edit and improve the provided document according to the user's "
67
  "instructions while preserving meaning, structure, headings, lists, and tone. "
68
  "Do not include any preface, summary, or explanation. "
69
- "Return only JSON with a single field 'enhanced_text'. "
70
- "If you add any extra commentary, it will be ignored.\n"
71
- "Optionally, also wrap the final edited document between markers [[[DOC]]] and [[[/DOC]]] "
72
- "if you must return any non-JSON content."
73
  )
74
 
75
  user = f"""User instructions:
@@ -79,54 +171,22 @@ Original document:
79
  {extracted_text}
80
  """
81
 
 
82
  try:
83
- resp = client.chat.completions.create(
84
- model=model_name,
85
- messages=[
86
- {"role": "system", "content": system},
87
- {"role": "user", "content": user},
88
- ],
89
- temperature=0.1,
90
- top_p=1.0,
91
- max_tokens=8192,
92
- response_format={"type": "json_object"}, # enforce JSON
93
- timeout=timeout_s,
94
- )
95
-
96
- content = (resp.choices[0].message.content or "").strip()
97
-
98
- # Expect a JSON object like {"enhanced_text": "..."}
99
- try:
100
- obj = json.loads(content)
101
- out = obj.get("enhanced_text")
102
- if isinstance(out, str) and out.strip():
103
- out = out.strip()
104
- else:
105
- out = content
106
- except json.JSONDecodeError:
107
- # If model ignored JSON, use raw (then clamp below)
108
- out = content
109
-
110
- # Final clamps
111
- block = _extract_marked_block(out)
112
- if block:
113
- out = block
114
- out = _sanitize_preface(out)
115
-
116
- return out or extracted_text
117
-
118
  except Exception:
119
- # On any error, return original text
120
- return extracted_text
121
 
 
 
 
 
 
 
 
122
 
123
- def enhance_doc(
124
- extracted_text: str,
125
- user_prompt: str,
126
- nim_model: Optional[str] = None,
127
- ) -> str:
128
- """
129
- Public entrypoint: enhance via NIM only.
130
- Returns the enhanced text or the original text on failure.
131
- """
132
- return enhance_with_nim(extracted_text, user_prompt, model=nim_model)
 
1
  import os
2
  import json
3
  import re
4
+ from typing import Optional, Tuple
5
 
6
  from openai import OpenAI
7
 
 
8
  # Environment configuration
9
  NVIDIA_API_KEY = os.getenv("NVIDIA_API_KEY")
10
  NIM_BASE_URL = os.getenv("NIM_BASE_URL", "https://integrate.api.nvidia.com/v1")
 
12
 
13
  # Sanitization utilities
14
  _PREFACE_RE = re.compile(r"^(okay[, ]|sure[, ]|here(?:'|’)s|summary:?|note:?|context:)\b", re.I)
15
+ # Customize anchors to your domain. These help clamp to the main heading if present.
16
  _ANCHOR_RE = re.compile(r"\b(meeting\s*minutes|minutes\s*of\s*meeting|invoice|report|summary)\b", re.I)
17
  _DOC_BLOCK_RE = re.compile(r"\[\[\[DOC\]\]\](.*)\[\[\[\/DOC\]\]\]", re.S)
18
 
 
19
  def _sanitize_preface(text: str) -> str:
 
20
  s = (text or "").lstrip()
 
 
21
  lines = s.splitlines()
22
  while lines and _PREFACE_RE.match(lines[0].strip()):
23
  lines.pop(0)
24
  s = "\n".join(lines).lstrip()
25
 
 
26
  m = _ANCHOR_RE.search(s)
27
  if m:
28
  s = s[m.start():]
29
 
30
  return s.strip()
31
 
 
32
  def _extract_marked_block(text: str) -> Optional[str]:
 
33
  m = _DOC_BLOCK_RE.search(text or "")
34
  if m:
35
  return m.group(1).strip()
36
  return None
37
 
38
+ def _finalize(out: str, fallback: str) -> str:
39
+ # Hard clamp to markers if present
40
+ block = _extract_marked_block(out)
41
+ if block:
42
+ out = block
43
+ out = _sanitize_preface(out)
44
+ return out or fallback
45
+
46
+ def _nim_call_with_tools(
47
+ client: OpenAI, model_name: str, system: str, user: str, timeout_s: int
48
+ ) -> str:
49
+ """
50
+ Strict mode: require a function call so the model must return arguments only.
51
+ Parse tool_calls and extract enhanced_text from function.arguments.
52
+ """
53
+ resp = client.chat.completions.create(
54
+ model=model_name,
55
+ messages=[
56
+ {"role": "system", "content": system},
57
+ {"role": "user", "content": user},
58
+ ],
59
+ temperature=0.0,
60
+ top_p=1.0,
61
+ max_tokens=8192,
62
+ tools=[{
63
+ "type": "function",
64
+ "function": {
65
+ "name": "return_enhanced_text",
66
+ "description": "Return the enhanced document text only.",
67
+ "parameters": {
68
+ "type": "object",
69
+ "properties": {"enhanced_text": {"type": "string"}},
70
+ "required": ["enhanced_text"]
71
+ }
72
+ }
73
+ }],
74
+ # Require the function; some NIM deployments fully support this,
75
+ # others may error. We'll catch and fallback if needed.
76
+ tool_choice={"type": "function", "function": {"name": "return_enhanced_text"}},
77
+ timeout=timeout_s,
78
+ )
79
+
80
+ msg = resp.choices[0].message
81
+ # OpenAI-compatible: tool_calls array with function name + arguments JSON
82
+ tool_calls = getattr(msg, "tool_calls", None)
83
+ if tool_calls:
84
+ for tc in tool_calls:
85
+ fn = tc.function
86
+ if fn and fn.name == "return_enhanced_text":
87
+ try:
88
+ args = json.loads(fn.arguments or "{}")
89
+ val = args.get("enhanced_text")
90
+ if isinstance(val, str) and val.strip():
91
+ return val.strip()
92
+ except Exception:
93
+ pass
94
+
95
+ # If the provider didn’t do a tool call or arguments failed to parse,
96
+ # try content JSON as a fallback path in this same response.
97
+ content = (msg.content or "").strip()
98
+ if content:
99
+ try:
100
+ obj = json.loads(content)
101
+ val = obj.get("enhanced_text")
102
+ if isinstance(val, str) and val.strip():
103
+ return val.strip()
104
+ except json.JSONDecodeError:
105
+ pass
106
+
107
+ # No usable output
108
+ return (msg.content or "").strip()
109
+
110
+ def _nim_call_json_only(
111
+ client: OpenAI, model_name: str, system: str, user: str, timeout_s: int
112
+ ) -> str:
113
+ """
114
+ JSON-only mode: enforce response_format={"type": "json_object"} and parse enhanced_text.
115
+ """
116
+ resp = client.chat.completions.create(
117
+ model=model_name,
118
+ messages=[
119
+ {"role": "system", "content": system},
120
+ {"role": "user", "content": user},
121
+ ],
122
+ temperature=0.1,
123
+ top_p=1.0,
124
+ max_tokens=8192,
125
+ response_format={"type": "json_object"},
126
+ timeout=timeout_s,
127
+ )
128
+ content = (resp.choices[0].message.content or "").strip()
129
+ try:
130
+ obj = json.loads(content)
131
+ val = obj.get("enhanced_text")
132
+ if isinstance(val, str) and val.strip():
133
+ return val.strip()
134
+ except json.JSONDecodeError:
135
+ pass
136
+ return content
137
 
138
+ def enhance_doc(
139
  extracted_text: str,
140
  user_prompt: str,
141
+ nim_model: Optional[str] = None,
142
  timeout_s: int = 60,
143
  ) -> str:
144
  """
145
+ Public entrypoint (NIM only):
146
+ 1) Try strict function-calling
147
+ 2) Fallback to JSON-only
148
+ 3) Final clamps (markers + preface removal)
149
+ 4) Return enhanced text or original on failure
150
  """
151
  if not NVIDIA_API_KEY:
 
152
  return extracted_text
153
 
154
+ model_name = nim_model or NIM_MODEL_DEFAULT
155
  client = OpenAI(api_key=NVIDIA_API_KEY, base_url=NIM_BASE_URL)
156
 
157
  system = (
158
  "You are a professional document editor. Edit and improve the provided document according to the user's "
159
  "instructions while preserving meaning, structure, headings, lists, and tone. "
160
  "Do not include any preface, summary, or explanation. "
161
+ "Return the result only via a function call named 'return_enhanced_text' with a single string field "
162
+ "'enhanced_text'. If you cannot call the function, return only JSON with a single field 'enhanced_text'. "
163
+ "If you produce any extra commentary, it will be discarded.\n"
164
+ "If you must show raw text, wrap the final edited document strictly between [[[DOC]]] and [[[/DOC]]]."
165
  )
166
 
167
  user = f"""User instructions:
 
171
  {extracted_text}
172
  """
173
 
174
+ # 1) Strict function-calling attempt
175
  try:
176
+ out = _nim_call_with_tools(client, model_name, system, user, timeout_s)
177
+ if out:
178
+ return _finalize(out, extracted_text)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  except Exception:
180
+ # Some NIM deployments may not support tool_choice strictly; fall back.
181
+ pass
182
 
183
+ # 2) JSON-only attempt
184
+ try:
185
+ out = _nim_call_json_only(client, model_name, system, user, timeout_s)
186
+ if out:
187
+ return _finalize(out, extracted_text)
188
+ except Exception:
189
+ pass
190
 
191
+ # 3) Last resort
192
+ return extracted_text