()
| 80 | |
| 81 | |
| 82 | def main(): |
| 83 | args = parse_args() |
| 84 | base_url = args.url if args.url else spawn_server() |
| 85 | |
| 86 | # Large system prompt (~2K tokens) to make the prefill cost measurable. |
| 87 | system = "You are a precise coding assistant. " * 200 |
| 88 | |
| 89 | def chat(user_msg, max_tokens=8): |
| 90 | payload = { |
| 91 | "model": "luce-dflash", |
| 92 | "messages": [ |
| 93 | {"role": "system", "content": system}, |
| 94 | {"role": "user", "content": user_msg}, |
| 95 | ], |
| 96 | "max_tokens": max_tokens, "stream": False, |
| 97 | } |
| 98 | body = json.dumps(payload).encode() |
| 99 | req = urllib.request.Request( |
| 100 | f"{base_url}/v1/chat/completions", |
| 101 | data=body, headers={"Content-Type": "application/json"}) |
| 102 | t0 = time.time() |
| 103 | resp = urllib.request.urlopen(req, timeout=600) |
| 104 | data = json.loads(resp.read()) |
| 105 | dt = time.time() - t0 |
| 106 | return dt, data["choices"][0]["message"]["content"] |
| 107 | |
| 108 | # Turn 1: cold (cache miss → snapshot taken at end) |
| 109 | print("\n=== Turn 1 (cold) ===", flush=True) |
| 110 | t1, r1 = chat("What is 2+2?") |
| 111 | print(f"latency={t1:.2f}s reply={r1!r}") |
| 112 | |
| 113 | # Turn 2: same system prompt → cache HIT, only suffix prefilled |
| 114 | print("\n=== Turn 2 (warm) ===", flush=True) |
| 115 | t2, r2 = chat("What is the capital of France?") |
| 116 | print(f"latency={t2:.2f}s reply={r2!r}") |
| 117 | |
| 118 | # Turn 3: same system prompt, third user → still warm |
| 119 | print("\n=== Turn 3 (warm) ===", flush=True) |
| 120 | t3, r3 = chat("Tell me about Mars.") |
| 121 | print(f"latency={t3:.2f}s reply={r3!r}") |
| 122 | |
| 123 | # Verdict |
| 124 | print("\n=== Verdict ===", flush=True) |
| 125 | print(f"turn_1: {t1:.2f}s") |
| 126 | print(f"turn_2: {t2:.2f}s ratio_2/1={t2/t1:.2f}") |
| 127 | print(f"turn_3: {t3:.2f}s ratio_3/1={t3/t1:.2f}") |
| 128 | # Expect turn 2 and 3 prefill to be much faster (5K system prompt cached). |
| 129 | # Total wall is prefill + decode; decode is ~constant (small max_tokens). |
| 130 | # Conservative gate: ratio < 0.85 (turn 2 should be at least 15% faster). |
| 131 | ok = (t2 / t1) < 0.85 and (t3 / t1) < 0.85 |
| 132 | print("\nPASS" if ok else "FAIL: prefix cache did not visibly speed up subsequent turns") |
| 133 | sys.exit(0 if ok else 1) |
| 134 | |
| 135 | |
| 136 | if __name__ == "__main__": |
no test coverage detected