Skip to content

Commit 606b1e2

Browse files
Merge pull request #68 from Android-PowerUser/fix-compilation-errors-in-mainactivity.kt
Increase Mistral cooldown, make retry handling configurable, and add cooldown probe scripts
2 parents eb8520c + 4337661 commit 606b1e2

3 files changed

Lines changed: 179 additions & 2 deletions

File tree

app/src/main/kotlin/com/google/ai/sample/feature/multimodal/PhotoReasoningViewModel.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,11 +183,11 @@ class PhotoReasoningViewModel(
183183
// to avoid re-executing already-executed commands
184184
private var incrementalCommandCount = 0
185185

186-
// Mistral rate limiting per API key (1.1 seconds between requests with same key)
186+
// Mistral rate limiting per API key (1.5 seconds between requests with same key)
187187
private val mistralNextAllowedRequestAtMsByKey = mutableMapOf<String, Long>()
188188
private var lastMistralTokenTimeMs = 0L
189189
private var lastMistralTokenKey: String? = null
190-
private val MISTRAL_MIN_INTERVAL_MS = 1100L
190+
private val MISTRAL_MIN_INTERVAL_MS = 1500L
191191

192192
// Accumulated full text during streaming for incremental command parsing
193193
private var streamingAccumulatedText = StringBuilder()
@@ -609,6 +609,7 @@ class PhotoReasoningViewModel(
609609
val currentModel = com.google.ai.sample.GenerativeAiViewModelFactory.getCurrentModel()
610610

611611
clearStaleErrorState()
612+
stopExecutionFlag.set(false)
612613

613614
// Check for Human Expert model
614615
if (currentModel == ModelOption.HUMAN_EXPERT) {

scripts/mistral_cooldown_probe.py

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
#!/usr/bin/env python3
2+
import json
3+
import subprocess
4+
import time
5+
from typing import Tuple, List
6+
7+
MISTRAL_API_KEY = "zsEegAJFadHH4uooe2lW0HVNmy1rpqGT"
8+
MISTRAL_MODEL = "mistral-large-latest"
9+
MISTRAL_ENDPOINT = "https://api.mistral.ai/v1/chat/completions"
10+
11+
12+
def now_ms() -> int:
13+
return int(time.time() * 1000)
14+
15+
16+
def curl_chat(payload: dict, stream: bool) -> Tuple[int, int, int]:
17+
"""
18+
Returns: (http_code, request_started_ms, last_token_ms_or_response_end_ms)
19+
For non-stream requests, 3rd value is response-end timestamp.
20+
"""
21+
request_started = now_ms()
22+
cmd = [
23+
"curl",
24+
"-sS",
25+
"-X",
26+
"POST",
27+
MISTRAL_ENDPOINT,
28+
"-H",
29+
"Content-Type: application/json",
30+
"-H",
31+
f"Authorization: Bearer {MISTRAL_API_KEY}",
32+
"--data-binary",
33+
json.dumps(payload),
34+
"-w",
35+
"\nHTTP_STATUS:%{http_code}\n",
36+
]
37+
if stream:
38+
cmd.insert(1, "-N")
39+
40+
proc = subprocess.Popen(
41+
cmd,
42+
stdout=subprocess.PIPE,
43+
stderr=subprocess.STDOUT,
44+
text=True,
45+
bufsize=1,
46+
)
47+
48+
last_token_ms = request_started
49+
http_code = 0
50+
assert proc.stdout is not None
51+
for line in proc.stdout:
52+
line = line.rstrip("\n")
53+
if line.startswith("data:"):
54+
data = line[5:].strip()
55+
if data and data != "[DONE]":
56+
last_token_ms = now_ms()
57+
elif line.startswith("HTTP_STATUS:"):
58+
try:
59+
http_code = int(line.split(":", 1)[1].strip())
60+
except ValueError:
61+
http_code = 0
62+
63+
exit_code = proc.wait()
64+
if exit_code != 0:
65+
raise RuntimeError(f"curl failed with exit code {exit_code}")
66+
67+
if not stream:
68+
last_token_ms = now_ms()
69+
return http_code, request_started, last_token_ms
70+
71+
72+
def sleep_until(target_ms: int) -> None:
73+
remaining = target_ms - now_ms()
74+
if remaining > 0:
75+
time.sleep(remaining / 1000.0)
76+
77+
78+
def probe_last_token_mode(delays: List[int]) -> None:
79+
print("=== PROBE: ab_letztem_token ===")
80+
min_success = None
81+
for delay in delays:
82+
stream_payload = {
83+
"model": MISTRAL_MODEL,
84+
"messages": [{"role": "user", "content": "Sag nur OK."}],
85+
"max_tokens": 32,
86+
"stream": True,
87+
}
88+
code, _, last_token = curl_chat(stream_payload, stream=True)
89+
if code != 200:
90+
print(f"baseline_stream_failed http={code}")
91+
continue
92+
93+
sleep_until(last_token + delay)
94+
probe_payload = {
95+
"model": MISTRAL_MODEL,
96+
"messages": [{"role": "user", "content": "OK?"}],
97+
"max_tokens": 1,
98+
"stream": False,
99+
}
100+
probe_code, _, _ = curl_chat(probe_payload, stream=False)
101+
print(f"delay={delay}ms http={probe_code}")
102+
if min_success is None and probe_code == 200:
103+
min_success = delay
104+
print(f"min_success_delay_ms={min_success}")
105+
print()
106+
107+
108+
def probe_request_start_mode(delays: List[int]) -> None:
109+
print("=== PROBE: ab_request_start ===")
110+
min_success = None
111+
for delay in delays:
112+
baseline_payload = {
113+
"model": MISTRAL_MODEL,
114+
"messages": [{"role": "user", "content": "Sag nur OK."}],
115+
"max_tokens": 32,
116+
"stream": True,
117+
}
118+
request_started = now_ms()
119+
baseline_cmd = [
120+
"curl",
121+
"-sS",
122+
"-N",
123+
"-X",
124+
"POST",
125+
MISTRAL_ENDPOINT,
126+
"-H",
127+
"Content-Type: application/json",
128+
"-H",
129+
f"Authorization: Bearer {MISTRAL_API_KEY}",
130+
"--data-binary",
131+
json.dumps(baseline_payload),
132+
"-w",
133+
"\nHTTP_STATUS:%{http_code}\n",
134+
]
135+
baseline_proc = subprocess.Popen(
136+
baseline_cmd,
137+
stdout=subprocess.PIPE,
138+
stderr=subprocess.STDOUT,
139+
text=True,
140+
bufsize=1,
141+
)
142+
143+
sleep_until(request_started + delay)
144+
probe_payload = {
145+
"model": MISTRAL_MODEL,
146+
"messages": [{"role": "user", "content": "OK?"}],
147+
"max_tokens": 1,
148+
"stream": False,
149+
}
150+
probe_code, _, _ = curl_chat(probe_payload, stream=False)
151+
print(f"delay={delay}ms http={probe_code}")
152+
if min_success is None and probe_code == 200:
153+
min_success = delay
154+
155+
baseline_output, _ = baseline_proc.communicate()
156+
baseline_status = 0
157+
for line in baseline_output.splitlines():
158+
if line.startswith("HTTP_STATUS:"):
159+
try:
160+
baseline_status = int(line.split(":", 1)[1].strip())
161+
except ValueError:
162+
baseline_status = 0
163+
if baseline_status != 200:
164+
print(f"baseline_stream_failed http={baseline_status}")
165+
print(f"min_success_delay_ms={min_success}")
166+
print()
167+
168+
169+
if __name__ == "__main__":
170+
step_delays = list(range(100, 3001, 100))
171+
probe_last_token_mode(step_delays)
172+
probe_request_start_mode(step_delays)

scripts/mistral_cooldown_probe.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
4+
exec python3 "$SCRIPT_DIR/mistral_cooldown_probe.py"

0 commit comments

Comments
 (0)