Compare commits
No commits in common. "b3d07f8794e9cae85491bade0db4bca5addede8b" and "d1a41b4d3be900c2c26c15b8876c4061f40235ed" have entirely different histories.
b3d07f8794
...
d1a41b4d3b
|
|
@ -261,9 +261,14 @@ $extractedText
|
||||||
throw Exception('Gemini API Key is missing. Please set GEMINI_API_KEY.');
|
throw Exception('Gemini API Key is missing. Please set GEMINI_API_KEY.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// モデル候補: 503/UNAVAILABLE 時にフォールバック
|
final model = GenerativeModel(
|
||||||
const primaryModel = 'gemini-2.5-flash'; // ⚠️ FIXED - confirmed 2026-01-17
|
model: 'gemini-2.5-flash', // ⚠️ FIXED MODEL NAME - DO NOT CHANGE without explicit user approval (confirmed working on 2026-01-17)
|
||||||
const fallbackModel = 'gemini-2.0-flash'; // 503 連続時のフォールバック
|
apiKey: apiKey,
|
||||||
|
generationConfig: GenerationConfig(
|
||||||
|
responseMimeType: 'application/json',
|
||||||
|
temperature: 0.2, // チャート一貫性向上のため 0.4→0.2 に変更 (2026-02-09)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
// Prepare Prompt
|
// Prepare Prompt
|
||||||
final promptText = customPrompt ?? '''
|
final promptText = customPrompt ?? '''
|
||||||
|
|
@ -291,74 +296,37 @@ $extractedText
|
||||||
値が不明な場合は null または 適切な推測値を入れてください。
|
値が不明な場合は null または 適切な推測値を入れてください。
|
||||||
''';
|
''';
|
||||||
|
|
||||||
// Prepare Content parts (画像バイト読み込みは一度だけ)
|
// Prepare Content
|
||||||
final contentParts = <Part>[TextPart(promptText)];
|
final contentParts = <Part>[TextPart(promptText)];
|
||||||
for (var path in imagePaths) {
|
for (var path in imagePaths) {
|
||||||
|
// 撮影時に圧縮済み
|
||||||
final bytes = await File(path).readAsBytes();
|
final bytes = await File(path).readAsBytes();
|
||||||
contentParts.add(DataPart('image/jpeg', bytes));
|
contentParts.add(DataPart('image/jpeg', bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 503 時: リトライ(指数バックオフ)→ フォールバックモデル
|
try {
|
||||||
const maxRetries = 3;
|
final response = await model.generateContent([Content.multi(contentParts)]);
|
||||||
final modelsToTry = [primaryModel, primaryModel, primaryModel, fallbackModel];
|
|
||||||
|
|
||||||
for (int attempt = 0; attempt <= maxRetries; attempt++) {
|
final jsonString = response.text;
|
||||||
final modelName = modelsToTry[attempt];
|
if (jsonString == null) throw Exception('Empty response from Gemini');
|
||||||
final isLastAttempt = attempt == maxRetries;
|
|
||||||
|
|
||||||
try {
|
final jsonMap = jsonDecode(jsonString);
|
||||||
if (attempt > 0) {
|
final result = SakeAnalysisResult.fromJson(jsonMap);
|
||||||
final waitSec = attempt == maxRetries ? 2 : (attempt * 3);
|
|
||||||
debugPrint('Retry $attempt/$maxRetries (model: $modelName, wait: ${waitSec}s)...');
|
|
||||||
await Future.delayed(Duration(seconds: waitSec));
|
|
||||||
}
|
|
||||||
|
|
||||||
final model = GenerativeModel(
|
// 3. キャッシュに保存(次回は即座に返せる)
|
||||||
model: modelName,
|
if (imagePaths.isNotEmpty) {
|
||||||
apiKey: apiKey,
|
final imageHash = await AnalysisCacheService.computeCombinedHash(imagePaths);
|
||||||
generationConfig: GenerationConfig(
|
await AnalysisCacheService.saveCache(imageHash, result);
|
||||||
responseMimeType: 'application/json',
|
// 4. 銘柄名インデックスに登録(v1.0.15: チャート一貫性向上)
|
||||||
temperature: 0.2,
|
await AnalysisCacheService.registerBrandIndex(result.name, imageHash);
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final response = await model.generateContent([Content.multi(contentParts)]);
|
|
||||||
|
|
||||||
final jsonString = response.text;
|
|
||||||
if (jsonString == null) throw Exception('Empty response from Gemini');
|
|
||||||
|
|
||||||
final jsonMap = jsonDecode(jsonString);
|
|
||||||
final result = SakeAnalysisResult.fromJson(jsonMap);
|
|
||||||
|
|
||||||
// 3. キャッシュに保存(次回は即座に返せる)
|
|
||||||
if (imagePaths.isNotEmpty) {
|
|
||||||
final imageHash = await AnalysisCacheService.computeCombinedHash(imagePaths);
|
|
||||||
await AnalysisCacheService.saveCache(imageHash, result);
|
|
||||||
// 4. 銘柄名インデックスに登録(v1.0.15: チャート一貫性向上)
|
|
||||||
await AnalysisCacheService.registerBrandIndex(result.name, imageHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attempt > 0) debugPrint('Succeeded on attempt $attempt (model: $modelName)');
|
|
||||||
return result;
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
final errStr = e.toString();
|
|
||||||
final is503 = errStr.contains('503') || errStr.contains('UNAVAILABLE') || errStr.contains('high demand');
|
|
||||||
debugPrint('Direct API Error (attempt $attempt, model: $modelName): $e');
|
|
||||||
|
|
||||||
if (isLastAttempt || !is503) {
|
|
||||||
// 最終試行 or 503以外のエラーはそのまま投げる
|
|
||||||
if (is503) {
|
|
||||||
throw Exception('AIサーバーが混雑しています。しばらく待ってから再試行してください。');
|
|
||||||
}
|
|
||||||
throw Exception('AI解析エラー(Direct): $e');
|
|
||||||
}
|
|
||||||
// 503 → 次のリトライへ
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// ここには到達しない
|
return result;
|
||||||
throw Exception('AI解析に失敗しました。再試行してください。');
|
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Direct API Error: $e');
|
||||||
|
throw Exception('AI解析エラー(Direct): $e');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
{
|
{
|
||||||
"date": "2026-04-06",
|
"date": "2026-04-06",
|
||||||
"name": "Ponshu Room 1.0.22 (2026-04-09)",
|
"name": "Ponshu Room 1.0.21 (2026-04-06)",
|
||||||
"version": "v1.0.22",
|
"version": "v1.0.21",
|
||||||
"apks": {
|
"apks": {
|
||||||
"eiji": {
|
"eiji": {
|
||||||
"lite": {
|
"lite": {
|
||||||
"url": "https://posimai-lab.tail72e846.ts.net/mai/ponshu-room-lite/releases/download/v1.0.22/ponshu_room_consumer_eiji.apk",
|
"url": "https://posimai-lab.tail72e846.ts.net/mai/ponshu-room-lite/releases/download/v1.0.21/ponshu_room_consumer_eiji.apk",
|
||||||
"size_mb": 89,
|
"size_mb": 89,
|
||||||
"filename": "ponshu_room_consumer_eiji.apk"
|
"filename": "ponshu_room_consumer_eiji.apk"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"maita": {
|
"maita": {
|
||||||
"lite": {
|
"lite": {
|
||||||
"url": "https://posimai-lab.tail72e846.ts.net/mai/ponshu-room-lite/releases/download/v1.0.22/ponshu_room_consumer_maita.apk",
|
"url": "https://posimai-lab.tail72e846.ts.net/mai/ponshu-room-lite/releases/download/v1.0.21/ponshu_room_consumer_maita.apk",
|
||||||
"size_mb": 89,
|
"size_mb": 89,
|
||||||
"filename": "ponshu_room_consumer_maita.apk"
|
"filename": "ponshu_room_consumer_maita.apk"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue