3v324v23 commited on
Commit
0d0289f
·
1 Parent(s): 87bde04

Preserve instructions in large code prompts

Browse files
Files changed (3) hide show
  1. app.py +30 -7
  2. generation.py +22 -0
  3. test_generation.py +17 -1
app.py CHANGED
@@ -36,7 +36,7 @@ from transformers import (
36
  StoppingCriteria,
37
  StoppingCriteriaList,
38
  )
39
- from generation import merge_eos_token_ids
40
  from openai_compat import (
41
  indexed_tool_calls,
42
  normalize_messages,
@@ -62,12 +62,10 @@ MODEL = os.getenv(
62
 
63
  MAX_CONTEXT_TOKENS = int(os.getenv("MAX_CONTEXT_TOKENS", "16384"))
64
  MAX_NEW_TOKENS = int(os.getenv("MAX_NEW_TOKENS", "1024"))
 
65
  TOOL_PROTOCOL_MARKER = "OPENAI TOOL CALL FORMAT — MANDATORY"
66
 
67
- # Keep the most recent tool result and user instructions if a client sends a
68
- # conversation longer than this Space can serve reliably.
69
  tokenizer = AutoTokenizer.from_pretrained(MODEL)
70
- tokenizer.truncation_side = "left"
71
 
72
  print(f"Loading {MODEL} on ZeroGPU during startup...", flush=True)
73
  model = AutoModelForCausalLM.from_pretrained(
@@ -171,9 +169,34 @@ def gerar(
171
  prompt,
172
  return_tensors="pt",
173
  add_special_tokens=False,
174
- truncation=True,
175
- max_length=max(1, MAX_CONTEXT_TOKENS - output_tokens),
176
- ).to("cuda")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
 
178
  print(
179
  f"Generation started: input_tokens={inputs['input_ids'].shape[1]} "
 
36
  StoppingCriteria,
37
  StoppingCriteriaList,
38
  )
39
+ from generation import head_tail_token_counts, merge_eos_token_ids
40
  from openai_compat import (
41
  indexed_tool_calls,
42
  normalize_messages,
 
62
 
63
  MAX_CONTEXT_TOKENS = int(os.getenv("MAX_CONTEXT_TOKENS", "16384"))
64
  MAX_NEW_TOKENS = int(os.getenv("MAX_NEW_TOKENS", "1024"))
65
+ PRESERVED_PREFIX_TOKENS = int(os.getenv("PRESERVED_PREFIX_TOKENS", "4096"))
66
  TOOL_PROTOCOL_MARKER = "OPENAI TOOL CALL FORMAT — MANDATORY"
67
 
 
 
68
  tokenizer = AutoTokenizer.from_pretrained(MODEL)
 
69
 
70
  print(f"Loading {MODEL} on ZeroGPU during startup...", flush=True)
71
  model = AutoModelForCausalLM.from_pretrained(
 
169
  prompt,
170
  return_tensors="pt",
171
  add_special_tokens=False,
172
+ truncation=False,
173
+ )
174
+ input_budget = max(1, MAX_CONTEXT_TOKENS - output_tokens)
175
+ input_length = inputs["input_ids"].shape[1]
176
+ if input_length > input_budget:
177
+ head_tokens, tail_tokens = head_tail_token_counts(
178
+ input_length,
179
+ input_budget,
180
+ PRESERVED_PREFIX_TOKENS,
181
+ )
182
+ for key, value in inputs.items():
183
+ if (
184
+ isinstance(value, torch.Tensor)
185
+ and value.ndim == 2
186
+ and value.shape[1] == input_length
187
+ ):
188
+ parts = []
189
+ if head_tokens:
190
+ parts.append(value[:, :head_tokens])
191
+ if tail_tokens:
192
+ parts.append(value[:, -tail_tokens:])
193
+ inputs[key] = torch.cat(parts, dim=1)
194
+ print(
195
+ f"Prompt truncated head+tail: original={input_length} "
196
+ f"head={head_tokens} tail={tail_tokens}",
197
+ flush=True,
198
+ )
199
+ inputs = inputs.to("cuda")
200
 
201
  print(
202
  f"Generation started: input_tokens={inputs['input_ids'].shape[1]} "
generation.py CHANGED
@@ -5,6 +5,28 @@ from __future__ import annotations
5
  from collections.abc import Iterable
6
 
7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  def ensure_bos_token(prompt: str, bos_token: str | None) -> str:
9
  """Prefix the model's BOS token when the chat template omits it."""
10
  if not bos_token or prompt.startswith(bos_token):
 
5
  from collections.abc import Iterable
6
 
7
 
8
+ def head_tail_token_counts(
9
+ total_tokens: int,
10
+ token_budget: int,
11
+ preserved_prefix_tokens: int,
12
+ ) -> tuple[int, int]:
13
+ """Split an oversized prompt budget between its prefix and recent tail.
14
+
15
+ Keeping only the tail can erase system instructions, tool definitions, and
16
+ a task stated before a large code block. Keeping a bounded prefix plus the
17
+ largest possible tail retains both the operating contract and the newest
18
+ conversation state.
19
+ """
20
+ total = max(0, int(total_tokens))
21
+ budget = max(1, int(token_budget))
22
+ if total <= budget:
23
+ return total, 0
24
+
25
+ prefix = max(0, int(preserved_prefix_tokens))
26
+ head = min(prefix, budget - 1)
27
+ return head, budget - head
28
+
29
+
30
  def ensure_bos_token(prompt: str, bos_token: str | None) -> str:
31
  """Prefix the model's BOS token when the chat template omits it."""
32
  if not bos_token or prompt.startswith(bos_token):
test_generation.py CHANGED
@@ -4,7 +4,11 @@ from __future__ import annotations
4
 
5
  import unittest
6
 
7
- from generation import ensure_bos_token, merge_eos_token_ids
 
 
 
 
8
 
9
 
10
  class GenerationConfigTests(unittest.TestCase):
@@ -30,6 +34,18 @@ class GenerationConfigTests(unittest.TestCase):
30
  def test_returns_none_without_valid_ids(self) -> None:
31
  self.assertIsNone(merge_eos_token_ids(None, None))
32
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
  if __name__ == "__main__":
35
  unittest.main()
 
4
 
5
  import unittest
6
 
7
+ from generation import (
8
+ ensure_bos_token,
9
+ head_tail_token_counts,
10
+ merge_eos_token_ids,
11
+ )
12
 
13
 
14
  class GenerationConfigTests(unittest.TestCase):
 
34
  def test_returns_none_without_valid_ids(self) -> None:
35
  self.assertIsNone(merge_eos_token_ids(None, None))
36
 
37
+ def test_short_prompt_is_not_split(self) -> None:
38
+ self.assertEqual(head_tail_token_counts(100, 200, 50), (100, 0))
39
+
40
+ def test_large_prompt_preserves_prefix_and_maximizes_tail(self) -> None:
41
+ self.assertEqual(
42
+ head_tail_token_counts(30_000, 16_000, 4_096),
43
+ (4_096, 11_904),
44
+ )
45
+
46
+ def test_tiny_budget_still_preserves_latest_token(self) -> None:
47
+ self.assertEqual(head_tail_token_counts(100, 1, 4_096), (0, 1))
48
+
49
 
50
  if __name__ == "__main__":
51
  unittest.main()