From ef7fccb255b0023e084ea9da84529489ff61110f Mon Sep 17 00:00:00 2001 From: posimai Date: Mon, 20 Apr 2026 21:17:07 +0900 Subject: [PATCH] =?UTF-8?q?fix(together):=20Gemini=E5=A4=B1=E6=95=97?= =?UTF-8?q?=E6=99=82=E3=82=82Jina=E6=88=90=E5=8A=9F=E3=81=AA=E3=82=89archi?= =?UTF-8?q?ve=5Fstatus=3D'done'=E3=81=AB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gemini API 503 エラー時に全体を'failed'にしていたため、 Jina で本文取得済みの記事でもReaderボタンが表示されなかった。 Gemini だけ失敗した場合は summary=null/tags=[] で 'done' にし、 Readerは使える状態を保つ。既存の28件は DB で直接修正済み。 Co-Authored-By: Claude Sonnet 4.6 --- server.js | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/server.js b/server.js index 743ed118..ac8f74d9 100644 --- a/server.js +++ b/server.js @@ -2583,21 +2583,25 @@ ${excerpt} let summary = null; let tags = []; if (genAI && fullContent) { - // 最初の ## 見出し以降を本文とみなし 4000 字を Gemini に渡す - const bodyStart = fullContent.search(/^#{1,2}\s/m); - const excerpt = (bodyStart >= 0 ? fullContent.slice(bodyStart) : fullContent).slice(0, 4000); - const model = genAI.getGenerativeModel({ model: 'gemini-2.5-flash' }); - const prompt = `以下の記事を分析して、JSONのみを返してください(コードブロック不要)。\n\n{"summary":"1〜2文の日本語要約","tags":["タグ1","タグ2","タグ3"]}\n\n- summary: 読者が読む価値があるかを判断できる1〜2文\n- tags: 内容を表す具体的な日本語タグを2〜4個。「その他」は絶対に使わないこと。内容が不明な場合でも最も近いカテゴリを選ぶ(例: AI, テクノロジー, ビジネス, 健康, 旅行, 料理, スポーツ, 政治, 経済, エンタメ, ゲーム, 科学, デザイン, ライフスタイル, 教育, 環境, 医療, 法律, 文化, 歴史)\n\n記事:\n${excerpt}`; - const timeoutP = new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), 30000)); - const result = await Promise.race([model.generateContent(prompt), timeoutP]); - const raw = result.response.text().trim(); try { - const parsed = JSON.parse(raw); - summary = (parsed.summary || '').slice(0, 300); - tags = Array.isArray(parsed.tags) ? parsed.tags.slice(0, 4).map(t => String(t).slice(0, 20)) : []; - } catch { - // JSON パース失敗時は全文を要約として扱う - summary = raw.slice(0, 300); + // 最初の ## 見出し以降を本文とみなし 4000 字を Gemini に渡す + const bodyStart = fullContent.search(/^#{1,2}\s/m); + const excerpt = (bodyStart >= 0 ? fullContent.slice(bodyStart) : fullContent).slice(0, 4000); + const model = genAI.getGenerativeModel({ model: 'gemini-2.5-flash' }); + const prompt = `以下の記事を分析して、JSONのみを返してください(コードブロック不要)。\n\n{"summary":"1〜2文の日本語要約","tags":["タグ1","タグ2","タグ3"]}\n\n- summary: 読者が読む価値があるかを判断できる1〜2文\n- tags: 内容を表す具体的な日本語タグを2〜4個。「その他」は絶対に使わないこと。内容が不明な場合でも最も近いカテゴリを選ぶ(例: AI, テクノロジー, ビジネス, 健康, 旅行, 料理, スポーツ, 政治, 経済, エンタメ, ゲーム, 科学, デザイン, ライフスタイル, 教育, 環境, 医療, 法律, 文化, 歴史)\n\n記事:\n${excerpt}`; + const timeoutP = new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), 30000)); + const result = await Promise.race([model.generateContent(prompt), timeoutP]); + const raw = result.response.text().trim(); + try { + const parsed = JSON.parse(raw); + summary = (parsed.summary || '').slice(0, 300); + tags = Array.isArray(parsed.tags) ? parsed.tags.slice(0, 4).map(t => String(t).slice(0, 20)) : []; + } catch { + summary = raw.slice(0, 300); + } + } catch (aiErr) { + // Gemini 失敗(503等): Jina 本文は保存済みなので Reader は使える状態で done にする + console.error('[together archive AI]', shareId, aiErr.message); } }